AI Engineering

AI SDK 7 Migration: Upgrading from AI SDK 6 (What the Codemod Misses)

I upgraded a real AI SDK 6 app to AI SDK 7. The codemod fixed 15 lines in 20 seconds but only 1 of 9 broken tests. Here's every step, the behaviour changes TypeScript misses, and the OpenTelemetry trap.

AI SDK 7 migration cover: terminal showing 6 type errors and 9 failing tests after npm i ai@7, 2 and 8 after the codemod, and 0 and 13 of 13 after manual fixes
On this page
  1. TL;DR: how do I upgrade to AI SDK 7?
  2. What's new in AI SDK 7?
  3. How I tested the upgrade
  4. Step 1: bump the packages (6 type errors, 9 failing tests)
  5. Step 2: run the codemod (2 type errors, 8 failing tests)
  6. Step 3: the behaviour changes you have to fix by hand
  7. Does OpenTelemetry still work after upgrading to AI SDK 7?
  8. Which AI SDK 7 changes does the codemod catch?
  9. Should I use the AI SDK migration skill?
  10. Install size and performance: is AI SDK 7 heavier?
  11. Other AI SDK 7 changes worth checking
  12. Common AI SDK 7 migration mistakes
  13. Key takeaways

An AI SDK 7 migration looks like a rename job: system becomes instructions, onFinish becomes onEnd, and there's a codemod for most of it. I upgraded a real AI SDK 6 app to see what's actually left after the codemod. The codemod ran in 20 seconds and fixed 15 lines, but it fixed only 1 of the 9 tests that broke. The rest were behaviour changes that TypeScript doesn't flag at all, including token usage that silently doubled and OpenTelemetry traces that silently stopped.

Here's the full upgrade path from AI SDK 6 to 7, with every number measured on ai 6.0.309 → 7.0.143.

TL;DR: how do I upgrade to AI SDK 7?#

  1. Make sure you're on Node.js 22 or later (24 LTS is better). AI SDK 7 drops Node 18 and 20.
  2. Commit, then upgrade: npm i ai@7 @ai-sdk/react@4 @ai-sdk/openai@4 (and your other providers).
  3. Run npx @ai-sdk/codemod v7 from the project root.
  4. Run tsc --noEmit and fix what's left, mainly tool context.
  5. Review the behaviour changes by hand: usage and toolCalls now cover all steps, step.response.messages is per step, onChunk gets every stream part, and system messages inside messages now throw.
  6. If you use OpenTelemetry, install @ai-sdk/otel and call registerTelemetry(), or your traces stop.
StageTypeScript errorsFailing tests (of 13)Lines changed
v6 baseline (ai 6.0.309)00none
Bump to ai 7.0.143, no code changes690
After npx @ai-sdk/codemod v72815
After manual fixes00+18 more

What's new in AI SDK 7?#

AI SDK 7.0 shipped on June 25, 2026, and the current release is ai 7.0.143. The headline changes in the official migration guide are:

  • Node.js 22+ and ESM-only packages. No more CommonJS builds.
  • Renames out of experimental_: telemetry, include, onStart, onStepStart, transcribe, generateSpeech, and tool context.
  • New names for old concepts: system → instructions, onFinish → onEnd, onStepFinish → onStepEnd, fullStream → stream, stepCountIs → isStepCount.
  • Results cover every step. usage, toolCalls, toolResults, content and friends are accumulated across a multi-step tool loop, and the old final-step values move to result.finalStep.
  • Typed, per-tool context with contextSchema and toolsContext, separate from a shared runtimeContext.
  • Telemetry as a plug-in. OpenTelemetry lives in @ai-sdk/otel, and telemetry is on by default once you register an integration.
  • Safer defaults: system messages in messages are rejected, request bodies aren't kept in results, and MCP HTTP transports no longer follow redirects.

Most renames keep the old name as a deprecated alias, so your code keeps compiling. That's convenient, and it's also why a type check alone won't tell you whether the migration is done.

How I tested the upgrade#

I wrote a small but realistic AI SDK 6 app, the kind of code you'd find in a Next.js chat product:

  • a tool loop with generateText, a system prompt, a weather tool reading experimental_context, stopWhen: stepCountIs(3), onStepFinish and onFinish callbacks logging token usage, and experimental_telemetry
  • a streaming chat route using streamText, convertToModelMessages and toUIMessageStreamResponse(), plus a fullStream consumer with an onChunk handler
  • structured output with experimental_output: Output.object(...)
  • stored chats whose first message is { role: 'system' }
  • a small CommonJS script that does require('ai')

Then I wrote 13 tests with node:test and the SDK's own MockLanguageModelV3, so every run is offline and deterministic. Each test pins down one behaviour the app relies on: the tool gets its context, usage is the final step's, the last step's messages hold the whole exchange, and so on. All 13 passed on v6 with zero TypeScript errors.

Versions: ai 6.0.309 → 7.0.143, @ai-sdk/openai 3.0.130 → 4.0.96, @ai-sdk/react 3.0.312 → 4.0.147, @ai-sdk/codemod 4.0.3, TypeScript 5.9.3, Node 24.22.

Step 1: bump the packages (6 type errors, 9 failing tests)#

Upgrading the packages without touching code gave 6 TypeScript errors:

  • experimental_context is gone from both the generateText options and the tool's execute options (2 errors)
  • experimental_output is gone as an option and as a result property (2 errors)
  • usage.cachedInputTokens no longer exists
  • the ToolCallOptions type is no longer exported

And 9 of 13 tests failed. The scariest failure was one that didn't look like a failure: with experimental_context gone, the tool threw Cannot destructure property 'units' of 'experimental_context' as it is undefined. The SDK turned that into an error-text tool result, sent it back to the model, and my mock model still produced a normal answer. The "tool loop works" test passed. A real model would probably apologise or guess, and you'd find out from users.

The CommonJS script, on the other hand, kept working. AI SDK 7 ships only ESM, but Node 24 can require() ES modules natively, so require('ai') still returned the functions. On older Node versions without require(esm) support it will fail, but those versions aren't supported by AI SDK 7 anyway.

Step 2: run the codemod (2 type errors, 8 failing tests)#

bash
npx @ai-sdk/codemod v7 src test

It took 19.8 seconds and changed 15 lines in 4 files:

  • system → instructions (2 places) and stepCountIs → isStepCount
  • onStepFinish → onStepEnd, onFinish → onEnd
  • experimental_telemetry → telemetry
  • usage.cachedInputTokens → usage.inputTokenDetails.cacheReadTokens
  • experimental_output → output (option and result)
  • fullStream → stream
  • ToolCallOptions → ToolExecutionOptions
  • { experimental_context } → { context: experimental_context } inside the tool's execute

Those are all correct, mechanical renames. What it couldn't do:

  • Tool context at the call site. It left experimental_context: { units: 'c' } in the generateText call, which is now a type error. The v7 design needs a contextSchema on the tool and a toolsContext object keyed by tool name, which isn't a rename.
  • The new generic. It swapped in ToolExecutionOptions, which now requires a type argument, so the codemod itself introduced a new type error.
  • Any behaviour change. It kept telemetry: { isEnabled: true } and kept reading result.usage and result.toolCalls exactly as before, now with different meanings.

The only test it fixed was structured output. Eight still failed.

Step 3: the behaviour changes you have to fix by hand#

AI SDK 6 to 7 migration steps: TypeScript errors and failing tests after the version bump, the codemod and manual fixes

These are the changes that broke tests even after the codemod, with the fix I used for each.

Tool context. Declare what each tool needs and pass it per tool:

ts
export const weather = tool({
  inputSchema: z.object({ city: z.string() }),
  contextSchema: z.object({ units: z.enum(['c', 'f']) }),
  execute: async ({ city }, { context: { units } }) => getWeather(city, units),
})

await generateText({
  model,
  tools: { weather },
  toolsContext: { weather: { units: 'c' } },
  // ...
})

The bonus is that context is now typed, so the as { units: ... } cast goes away.

Usage now covers all steps. In my two-step tool loop, result.usage.totalTokens went from 15 (final step only) to 30 (both steps), and cached input tokens went from 4 to 8. totalUsage is deprecated. If your billing, rate limits or logs read usage, decide which number you want. For the old meaning, read result.finalStep.usage. The same applies inside onEnd: event.usage is now all steps and event.finalStep.usage is the last step.

toolCalls and friends now cover all steps. result.toolCalls.length went from 0 to 1, because the tool call happened in step 1, not the final step. Same for toolResults, content, files, sources and warnings. Use result.finalStep.toolCalls for the old behaviour.

step.response.messages is per step. In v6 the last step's response.messages held the whole exchange (3 messages: tool call, tool result, answer). In v7 it held 1. If you persist the conversation from it, switch to result.responseMessages.

onChunk receives every stream part. My handler went from seeing 2 chunks (text-delta ×2) to 8, including start, start-step, text-start, text-end, finish-step and finish. Guard on chunk.type before using it.

System messages in messages throw. v6 printed a warning; v7 throws AI_InvalidPromptError: System messages are not allowed in the prompt or messages fields. That breaks old stored chats. I moved them into instructions:

ts
const instructions = stored.filter(m => m.role === 'system').map(m => m.content).join('\n')
const messages = stored.filter(m => m.role !== 'system')
await generateText({ model, instructions: instructions || undefined, messages })

Only use allowSystemInMessages: true if every stored message came from your own server code, because a user-supplied system message is a prompt injection.

Deprecated stream helpers. result.toUIMessageStreamResponse() still works but is deprecated. The stateless replacement is createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }) }).

With these 18 extra lines across the same 4 files, I was back to 0 TypeScript errors, 13 of 13 tests passing and no deprecation warnings.

Does OpenTelemetry still work after upgrading to AI SDK 7?#

Not by itself. This was the most silent break I found, and none of my tests caught it, so I tested it separately with an in-memory OpenTelemetry exporter.

  • v6 with experimental_telemetry: { isEnabled: true } recorded 2 spans: ai.generateText and ai.generateText.doGenerate.
  • v7, same code, recorded 0 spans. No error, no warning.
  • v7 with @ai-sdk/otel 1.0.143 and registerTelemetry(new OpenTelemetry()) at startup recorded 3 spans, with new names: invoke_agent mock-model-id, chat mock-model-id and step 1.

So you need the new package and you need to update any dashboard, alert or query that filters on the old ai.generateText span names. For Next.js, register it in instrumentation.ts:

ts
import { registerTelemetry } from 'ai'
import { OpenTelemetry } from '@ai-sdk/otel'

registerTelemetry(new OpenTelemetry())

Once an integration is registered, every call is traced by default, so you can delete isEnabled: true and use isEnabled: false to opt out.

Which AI SDK 7 changes does the codemod catch?#

Which AI SDK 7 changes cause a type error, are fixed by the codemod, or are only caught by tests

The pattern is clear. Renames are covered: either the old name still works as an alias, or TypeScript flags it and the codemod fixes it. Semantic changes are not: all-steps results, per-step messages, onChunk and system messages pass the type check and slip past the codemod. Telemetry gets past all three.

If your app has no tests around its AI calls, write a few before you upgrade. Pin down token usage, what you persist, and what your tools receive.

Should I use the AI SDK migration skill?#

Vercel also publishes an agent skill for this upgrade:

bash
npx skills add vercel/ai --skill migrate-ai-sdk-v6-to-v7

Then you ask your coding agent to "use the migrate-ai-sdk-v6-to-v7 skill and migrate my app". The skill is a checklist that points at the migration guide, and it does say the right thing: "When v7 changes semantics, decide whether the app wants the new all-steps behavior or the previous final-step-only behavior." It also covers tool context, finalStep and @ai-sdk/otel, which the codemod can't.

My suggestion: run the codemod first for the mechanical renames, then let an agent with the skill (or yourself, with this list) work through the behaviour changes, and review that diff carefully. If you haven't used skills before, I explained how they work in Agent Skills explained.

Install size and performance: is AI SDK 7 heavier?#

Barely. A fresh install of ai, @ai-sdk/openai, @ai-sdk/react and React was 23.8 MB across 18 packages on v6 and 24.3 MB across 26 packages on v7. The ai package itself grew from 7.2 MB to 8.2 MB. It dropped its @opentelemetry/api dependency (now in @ai-sdk/otel), while @ai-sdk/react 4 now pulls in @ai-sdk/mcp and cross-spawn.

Import time didn't change: import 'ai' took 113.5 ms on v6 and 115.4 ms on v7 (30 runs each with hyperfine, against 23.8 ms for an empty Node script), which is within noise.

Other AI SDK 7 changes worth checking#

My test app didn't use these, but grep your code for them:

  • prepareStep overrides carry forward. Returned instructions and messages now stick for later steps instead of applying to one step.
  • Request and response bodies are excluded by default. If you log result.request.body, opt in with include: { requestBody: true }.
  • MCP redirects. createMCPClient HTTP and SSE transports now default to redirect: 'error' to block SSRF. If your MCP server redirects, set redirect: 'follow'. See my MCP security guide for why that default matters.
  • needsApproval is deprecated in favour of a toolApproval setting on the call.
  • Image parts become { type: 'file', mediaType: 'image', data }.
  • xAI now uses the Responses API by default, and OpenAI reasoning summaries default to 'detailed' when reasoning is on.
  • Google provider names drop GenerativeAI, for example createGoogleGenerativeAI → createGoogle.

Common AI SDK 7 migration mistakes#

  • Stopping when tsc passes. Most of the deprecated names still compile. My app had 0 type errors with 8 failing tests in between.
  • Not reviewing the meaning of usage. It now covers all steps. Code that adds up per-step usage and then also adds the final usage will now count every step twice.
  • Forgetting @ai-sdk/otel. Traces stop without an error.
  • Trusting a passing "it answers" test. A tool that throws becomes a tool error the model can talk around.
  • Turning on allowSystemInMessages for user data. Move system prompts to instructions instead.

Key takeaways#

  • AI SDK 7 needs Node.js 22+ and is ESM-only, but require('ai') still worked on Node 24 thanks to native require(esm).
  • Bumping ai 6.0.309 → 7.0.143 gave 6 TypeScript errors and 9 of 13 failing tests on a realistic app.
  • npx @ai-sdk/codemod v7 took 20 seconds and fixed 15 lines, but only 1 of the 9 failing tests.
  • usage, toolCalls and the other results now cover all steps; in my tool loop, usage went from 15 to 30 tokens. Use finalStep for the old meaning.
  • Tool context moved to contextSchema plus toolsContext; without it, the tool failed silently while the answer still looked fine.
  • OpenTelemetry recorded 0 spans after the upgrade until I added @ai-sdk/otel, and the span names changed.
  • Install size and import time are basically unchanged: 24.3 MB vs 23.8 MB and 115 ms vs 114 ms.

Related: my open-source Next.js AI starter kit, Forgekit, already runs on AI SDK 7 (it uses isStepCount), so you can compare against a working v7 chat app.

Questions