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.

On this page
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?#
- Make sure you're on Node.js 22 or later (24 LTS is better). AI SDK 7 drops Node 18 and 20.
- Commit, then upgrade:
npm i ai@7 @ai-sdk/react@4 @ai-sdk/openai@4(and your other providers). - Run
npx @ai-sdk/codemod v7from the project root. - Run
tsc --noEmitand fix what's left, mainly tool context. - Review the behaviour changes by hand:
usageandtoolCallsnow cover all steps,step.response.messagesis per step,onChunkgets every stream part, and system messages insidemessagesnow throw. - If you use OpenTelemetry, install
@ai-sdk/oteland callregisterTelemetry(), or your traces stop.
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 toolcontext. - New names for old concepts:
system→instructions,onFinish→onEnd,onStepFinish→onStepEnd,fullStream→stream,stepCountIs→isStepCount. - Results cover every step.
usage,toolCalls,toolResults,contentand friends are accumulated across a multi-step tool loop, and the old final-step values move toresult.finalStep. - Typed, per-tool context with
contextSchemaandtoolsContext, separate from a sharedruntimeContext. - 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
messagesare 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, asystemprompt, a weather tool readingexperimental_context,stopWhen: stepCountIs(3),onStepFinishandonFinishcallbacks logging token usage, andexperimental_telemetry - a streaming chat route using
streamText,convertToModelMessagesandtoUIMessageStreamResponse(), plus afullStreamconsumer with anonChunkhandler - 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_contextis gone from both thegenerateTextoptions and the tool'sexecuteoptions (2 errors)experimental_outputis gone as an option and as a result property (2 errors)usage.cachedInputTokensno longer exists- the
ToolCallOptionstype 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)#
npx @ai-sdk/codemod v7 src testIt took 19.8 seconds and changed 15 lines in 4 files:
system→instructions(2 places) andstepCountIs→isStepCountonStepFinish→onStepEnd,onFinish→onEndexperimental_telemetry→telemetryusage.cachedInputTokens→usage.inputTokenDetails.cacheReadTokensexperimental_output→output(option and result)fullStream→streamToolCallOptions→ToolExecutionOptions{ experimental_context }→{ context: experimental_context }inside the tool'sexecute
Those are all correct, mechanical renames. What it couldn't do:
- Tool context at the call site. It left
experimental_context: { units: 'c' }in thegenerateTextcall, which is now a type error. The v7 design needs acontextSchemaon the tool and atoolsContextobject 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 readingresult.usageandresult.toolCallsexactly 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#

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:
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:
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.generateTextandai.generateText.doGenerate. - v7, same code, recorded 0 spans. No error, no warning.
- v7 with
@ai-sdk/otel1.0.143 andregisterTelemetry(new OpenTelemetry())at startup recorded 3 spans, with new names:invoke_agent mock-model-id,chat mock-model-idandstep 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:
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?#

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:
npx skills add vercel/ai --skill migrate-ai-sdk-v6-to-v7Then 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:
prepareStepoverrides carry forward. Returnedinstructionsandmessagesnow 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 withinclude: { requestBody: true }. - MCP redirects.
createMCPClientHTTP and SSE transports now default toredirect: 'error'to block SSRF. If your MCP server redirects, setredirect: 'follow'. See my MCP security guide for why that default matters. needsApprovalis deprecated in favour of atoolApprovalsetting 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 examplecreateGoogleGenerativeAI→createGoogle.
Common AI SDK 7 migration mistakes#
- Stopping when
tscpasses. 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 finalusagewill 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
allowSystemInMessagesfor user data. Move system prompts toinstructionsinstead.
Key takeaways#
- AI SDK 7 needs Node.js 22+ and is ESM-only, but
require('ai')still worked on Node 24 thanks to nativerequire(esm). - Bumping
ai6.0.309 → 7.0.143 gave 6 TypeScript errors and 9 of 13 failing tests on a realistic app. npx @ai-sdk/codemod v7took 20 seconds and fixed 15 lines, but only 1 of the 9 failing tests.usage,toolCallsand the other results now cover all steps; in my tool loop, usage went from 15 to 30 tokens. UsefinalStepfor the old meaning.- Tool context moved to
contextSchemaplustoolsContext; 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
Share


