| name | ai-sdk-core |
| description | Backend AI functionality with Vercel AI SDK v5 - text generation, structured output with Zod, tool calling, and agents. Multi-provider support for OpenAI, Anthropic, Google, and Cloudflare Workers AI. Use when: implementing server-side AI features, generating text/chat completions, creating structured AI outputs with Zod schemas, building AI agents with tools, streaming AI responses, integrating OpenAI/Anthropic/Google/Cloudflare providers, or encountering AI SDK errors like AI_APICallError, AI_NoObjectGeneratedError, streaming failures, or worker startup limits. Keywords: ai sdk core, vercel ai sdk, generateText, streamText, generateObject, streamObject, ai sdk node, ai sdk server, zod ai schema, ai tools calling, ai agent class, openai sdk, anthropic sdk, google gemini sdk, workers-ai-provider, ai streaming backend, multi-provider ai, ai sdk errors, AI_APICallError, AI_NoObjectGeneratedError, streamText fails, worker startup limit ai |
| license | MIT |
AI SDK Core
Production-ready backend AI with Vercel AI SDK v5.
Quick Start (5 Minutes)
Installation
# Core package
npm install ai
# Provider packages (install what you need)
npm install @ai-sdk/openai # OpenAI (GPT-5, GPT-4, GPT-3.5)
npm install @ai-sdk/anthropic # Anthropic (Claude Sonnet 4.5, Opus 4, Haiku 4)
npm install @ai-sdk/google # Google (Gemini 2.5 Pro/Flash/Lite)
npm install workers-ai-provider # Cloudflare Workers AI
# Schema validation
npm install zod
Environment Variables
# .env
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_GENERATIVE_AI_API_KEY=...
First Example: Generate Text
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = await generateText({
model: openai('gpt-4-turbo'),
prompt: 'What is TypeScript?',
});
console.log(result.text);
First Example: Streaming Chat
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
const stream = streamText({
model: anthropic('claude-sonnet-4-5-20250929'),
messages: [
{ role: 'user', content: 'Tell me a story' },
],
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
First Example: Structured Output
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const result = await generateObject({
model: openai('gpt-4'),
schema: z.object({
name: z.string(),
age: z.number(),
skills: z.array(z.string()),
}),
prompt: 'Generate a person profile for a software engineer',
});
console.log(result.object);
// { name: "Alice", age: 28, skills: ["TypeScript", "React"] }
Core Functions
generateText()
Generate text completion with optional tools and multi-step execution.
Signature:
async function generateText(options: {
model: LanguageModel;
prompt?: string;
messages?: Array<ModelMessage>;
system?: string;
tools?: Record<string, Tool>;
maxOutputTokens?: number;
temperature?: number;
stopWhen?: StopCondition;
// ... other options
}): Promise<GenerateTextResult>
Basic Usage:
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = await generateText({
model: openai('gpt-4-turbo'),
prompt: 'Explain quantum computing',
maxOutputTokens: 500,
temperature: 0.7,
});
console.log(result.text);
console.log(`Tokens: ${result.usage.totalTokens}`);
With Messages (Chat Format):
const result = await generateText({
model: openai('gpt-4-turbo'),
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'What is the weather?' },
{ role: 'assistant', content: 'I need your location.' },
{ role: 'user', content: 'San Francisco' },
],
});
With Tools:
import { tool } from 'ai';
import { z } from 'zod';
const result = await generateText({
model: openai('gpt-4'),
tools: {
weather: tool({
description: 'Get the weather for a location',
inputSchema: z.object({
location: z.string(),
}),
execute: async ({ location }) => {
// API call here
return { temperature: 72, condition: 'sunny' };
},
}),
},
prompt: 'What is the weather in Tokyo?',
});
When to Use:
- Need final response (not streaming)
- Want to wait for tool executions to complete
- Simpler code when streaming not needed
- Building batch/scheduled tasks
Error Handling:
import { AI_APICallError, AI_NoContentGeneratedError } from 'ai';
try {
const result = await generateText({
model: openai('gpt-4-turbo'),
prompt: 'Hello',
});
console.log(result.text);
} catch (error) {
if (error instanceof AI_APICallError) {
console.error('API call failed:', error.message);
// Check rate limits, API key, network
} else if (error instanceof AI_NoContentGeneratedError) {
console.error('No content generated');
// Prompt may have been filtered
} else {
console.error('Unknown error:', error);
}
}
streamText()
Stream text completion with real-time chunks.
Signature:
function streamText(options: {
model: LanguageModel;
prompt?: string;
messages?: Array<ModelMessage>;
system?: string;
tools?: Record<string, Tool>;
maxOutputTokens?: number;
temperature?: number;
stopWhen?: StopCondition;
// ... other options
}): StreamTextResult
Basic Streaming:
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
const stream = streamText({
model: anthropic('claude-sonnet-4-5-20250929'),
prompt: 'Write a poem about AI',
});
// Stream to console
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
// Or get final result
const finalResult = await stream.result;
console.log(finalResult.text);
Streaming with Tools:
const stream = streamText({
model: openai('gpt-4'),
tools: {
// ... tools definition
},
prompt: 'What is the weather?',
});
// Stream text chunks
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
Handling the Stream:
const stream = streamText({
model: openai('gpt-4-turbo'),
prompt: 'Explain AI',
});
// Option 1: Text stream
for await (const text of stream.textStream) {
console.log(text);
}
// Option 2: Full stream (includes metadata)
for await (const part of stream.fullStream) {
if (part.type === 'text-delta') {
console.log(part.textDelta);
} else if (part.type === 'tool-call') {
console.log('Tool called:', part.toolName);
}
}
// Option 3: Wait for final result
const result = await stream.result;
console.log(result.text, result.usage);
When to Use:
- Real-time user-facing responses
- Long-form content generation
- Want to show progress
- Better perceived performance
Production Pattern:
// Next.js API Route
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
export async function POST(request: Request) {
const { messages } = await request.json();
const stream = streamText({
model: openai('gpt-4-turbo'),
messages,
});
// Return stream to client
return stream.toDataStreamResponse();
}
Error Handling:
// Recommended: Use onError callback (added in v4.1.22)
const stream = streamText({
model: openai('gpt-4-turbo'),
prompt: 'Hello',
onError({ error }) {
console.error('Stream error:', error);
// Custom error handling
},
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
// Alternative: Manual try-catch
try {
const stream = streamText({
model: openai('gpt-4-turbo'),
prompt: 'Hello',
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
} catch (error) {
console.error('Stream error:', error);
}
generateObject()
Generate structured output validated by Zod schema.
Signature:
async function generateObject<T>(options: {
model: LanguageModel;
schema: z.Schema<T>;
prompt?: string;
messages?: Array<ModelMessage>;
system?: string;
mode?: 'auto' | 'json' | 'tool';
// ... other options
}): Promise<GenerateObjectResult<T>>
Basic Usage:
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const result = await generateObject({
model: openai('gpt-4'),
schema: z.object({
recipe: z.object({
name: z.string(),
ingredients: z.array(z.object({
name: z.string(),
amount: z.string(),
})),
instructions: z.array(z.string()),
}),
}),
prompt: 'Generate a recipe for chocolate chip cookies',
});
console.log(result.object.recipe);
Nested Schemas:
const PersonSchema = z.object({
name: z.string(),
age: z.number(),
address: z.object({
street: z.string(),
city: z.string(),
country: z.string(),
}),
hobbies: z.array(z.string()),
});
const result = await generateObject({
model: openai('gpt-4'),
schema: PersonSchema,
prompt: 'Generate a person profile',
});
Arrays and Unions:
// Array of objects
const result = await generateObject({
model: openai('gpt-4'),
schema: z.object({
people: z.array(z.object({
name: z.string(),
role: z.enum(['engineer', 'designer', 'manager']),
})),
}),
prompt: 'Generate a team of 5 people',
});
// Union types
const result = await generateObject({
model: openai('gpt-4'),
schema: z.discriminatedUnion('type', [
z.object({ type: z.literal('text'), content: z.string() }),
z.object({ type: z.literal('image'), url: z.string() }),
]),
prompt: 'Generate content',
});
When to Use:
- Need structured data (JSON, forms, etc.)
- Validation is critical
- Extracting data from unstructured input
- Building AI workflows that consume JSON
Error Handling:
import { AI_NoObjectGeneratedError, AI_TypeValidationError } from 'ai';
try {
const result = await generateObject({
model: openai('gpt-4'),
schema: z.object({ name: z.string() }),
prompt: 'Generate a person',
});
} catch (error) {
if (error instanceof AI_NoObjectGeneratedError) {
console.error('Model did not generate valid object');
// Try simplifying schema or adding examples
} else if (error instanceof AI_TypeValidationError) {
console.error('Zod validation failed:', error.message);
// Schema doesn't match output
}
}
streamObject()
Stream structured output with partial updates.
Signature:
function streamObject<T>(options: {
model: LanguageModel;
schema: z.Schema<T>;
prompt?: string;
messages?: Array<ModelMessage>;
mode?: 'auto' | 'json' | 'tool';
// ... other options
}): StreamObjectResult<T>
Basic Usage:
import { streamObject } from 'ai';
import { google } from '@ai-sdk/google';
import { z } from 'zod';
const stream = streamObject({
model: google('gemini-2.5-pro'),
schema: z.object({
characters: z.array(z.object({
name: z.string(),
class: z.string(),
stats: z.object({
hp: z.number(),
mana: z.number(),
}),
})),
}),
prompt: 'Generate 3 RPG characters',
});
// Stream partial updates
for await (const partialObject of stream.partialObjectStream) {
console.log(partialObject);
// { characters: [{ name: "Aria" }] }
// { characters: [{ name: "Aria", class: "Mage" }] }
// { characters: [{ name: "Aria", class: "Mage", stats: { hp: 100 } }] }
// ...
}
UI Integration Pattern:
// Server endpoint
export async function POST(request: Request) {
const { prompt } = await request.json();
const stream = streamObject({
model: openai('gpt-4'),
schema: z.object({
summary: z.string(),
keyPoints: z.array(z.string()),
}),
prompt,
});
return stream.toTextStreamResponse();
}
// Client (with useObject hook from ai-sdk-ui)
const { object, isLoading } = useObject({
api: '/api/analyze',
schema: /* same schema */,
});
// Render partial object as it streams
{object?.summary && <p>{object.summary}</p>}
{object?.keyPoints?.map(point => <li key={point}>{point}</li>)}
When to Use:
- Real-time structured data (forms, dashboards)
- Show progressive completion
- Large structured outputs
- Better UX for slow generations
Provider Setup & Configuration
OpenAI
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
// API key from environment (recommended)
// OPENAI_API_KEY=sk-...
const model = openai('gpt-4-turbo');
// Or explicit API key
const model = openai('gpt-4', {
apiKey: process.env.OPENAI_API_KEY,
});
// Available models
const gpt5 = openai('gpt-5'); // Latest (released August 2025)
const gpt4 = openai('gpt-4-turbo');
const gpt35 = openai('gpt-3.5-turbo');
const result = await generateText({
model: gpt4,
prompt: 'Hello',
});
Common Errors:
AI_LoadAPIKeyError: CheckOPENAI_API_KEYenvironment variable429 Rate Limit: Implement exponential backoff, upgrade tier401 Unauthorized: Invalid API key format
Rate Limiting: OpenAI enforces RPM (requests per minute) and TPM (tokens per minute) limits. Implement retry logic:
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Hello',
maxRetries: 3, // Built-in retry
});
Anthropic
import { anthropic } from '@ai-sdk/anthropic';
// ANTHROPIC_API_KEY=sk-ant-...
const claude = anthropic('claude-sonnet-4-5-20250929');
// Available models (Claude 4.x family, released 2025)
const sonnet45 = anthropic('claude-sonnet-4-5-20250929'); // Latest, recommended
const sonnet4 = anthropic('claude-sonnet-4-20250522'); // Released May 2025
const opus4 = anthropic('claude-opus-4-20250522'); // Highest quality
// Legacy models (Claude 3.x, deprecated)
// const sonnet35 = anthropic('claude-3-5-sonnet-20241022'); // Use Claude 4.x instead
// const opus3 = anthropic('claude-3-opus-20240229');
// const haiku3 = anthropic('claude-3-haiku-20240307');
const result = await generateText({
model: sonnet45,
prompt: 'Explain quantum entanglement',
});
Common Errors:
AI_LoadAPIKeyError: CheckANTHROPIC_API_KEYenvironment variableoverloaded_error: Retry with exponential backoffrate_limit_error: Wait and retry
Best Practices:
- Claude excels at long-context tasks (200K+ tokens)
- Claude 4.x recommended: Anthropic deprecated Claude 3.x in 2025
- Use Sonnet 4.5 for balance of speed/quality (latest model)
- Use Sonnet 4 for production stability (if avoiding latest)
- Use Opus 4 for highest quality reasoning and complex tasks
import { google } from '@ai-sdk/google';
// GOOGLE_GENERATIVE_AI_API_KEY=...
const gemini = google('gemini-2.5-pro');
// Available models (all GA since June-July 2025)
const pro = google('gemini-2.5-pro');
const flash = google('gemini-2.5-flash');
const lite = google('gemini-2.5-flash-lite');
const result = await generateText({
model: pro,
prompt: 'Analyze this data',
});
Common Errors:
AI_LoadAPIKeyError: CheckGOOGLE_GENERATIVE_AI_API_KEYSAFETY: Content filtered by safety settingsQUOTA_EXCEEDED: Rate limit hit
Best Practices:
- Gemini Pro: Best for reasoning and analysis
- Gemini Flash: Fast, cost-effective for most tasks
- Free tier has generous limits
- Good for multimodal tasks (combine with image inputs)
Cloudflare Workers AI
import { Hono } from 'hono';
import { generateText } from 'ai';
import { createWorkersAI } from 'workers-ai-provider';
interface Env {
AI: Ai;
}
const app = new Hono<{ Bindings: Env }>();
app.post('/chat', async (c) => {
// Create provider inside handler (avoid startup overhead)
const workersai = createWorkersAI({ binding: c.env.AI });
const result = await generateText({
model: workersai('@cf/meta/llama-3.1-8b-instruct'),
prompt: 'What is Cloudflare?',
});
return c.json({ response: result.text });
});
export default app;
wrangler.jsonc:
{
"name": "ai-sdk-worker",
"compatibility_date": "2025-10-21",
"ai": {
"binding": "AI"
}
}
Important Notes:
Startup Optimization: AI SDK v5 + Zod can cause >270ms startup time in Workers. Solutions:
- Move imports inside handler:
// BAD (startup overhead)
import { createWorkersAI } from 'workers-ai-provider';
const workersai = createWorkersAI({ binding: env.AI });
// GOOD (lazy init)
app.post('/chat', async (c) => {
const { createWorkersAI } = await import('workers-ai-provider');
const workersai = createWorkersAI({ binding: c.env.AI });
// ...
});
- Minimize top-level Zod schemas:
// Move complex schemas into route handlers
When to Use workers-ai-provider:
- Multi-provider scenarios (OpenAI + Workers AI)
- Using AI SDK UI hooks with Workers AI
- Need consistent API across providers
When to Use Native Binding:
For Cloudflare-only deployments without multi-provider support, use the cloudflare-workers-ai skill instead for maximum performance.
Tool Calling & Agents
Basic Tool Definition
import { generateText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const result = await generateText({
model: openai('gpt-4'),
tools: {
weather: tool({
description: 'Get the weather for a location',
inputSchema: z.object({
location: z.string().describe('The city and country, e.g. "Paris, France"'),
unit: z.enum(['celsius', 'fahrenheit']).optional(),
}),
execute: async ({ location, unit = 'celsius' }) => {
// Simulate API call
const data = await fetch(`https://api.weather.com/${location}`);
return { temperature: 72, condition: 'sunny', unit };
},
}),
convertTemperature: tool({
description: 'Convert temperature between units',
inputSchema: z.object({
value: z.number(),
from: z.enum(['celsius', 'fahrenheit']),
to: z.enum(['celsius', 'fahrenheit']),
}),
execute: async ({ value, from, to }) => {
if (from === to) return { value };
if (from === 'celsius' && to === 'fahrenheit') {
return { value: (value * 9/5) + 32 };
}
return { value: (value - 32) * 5/9 };
},
}),
},
prompt: 'What is the weather in Tokyo in Fahrenheit?',
});
console.log(result.text);
// Model will call weather tool, potentially convertTemperature, then answer
v5 Tool Changes:
parameters→inputSchema(Zod schema)- Tool properties:
args→input,result→output ToolExecutionErrorremoved (nowtool-errorcontent parts)
Agent Class
The Agent class simplifies multi-step execution with tools.
import { Agent, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
const weatherAgent = new Agent({
model: anthropic('claude-sonnet-4-5-20250929'),
system: 'You are a weather assistant. Always convert temperatures to the user\'s preferred unit.',
tools: {
getWeather: tool({
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string(),
}),
execute: async ({ location }) => {
return { temp: 72, condition: 'sunny', unit: 'fahrenheit' };
},
}),
convertTemp: tool({
description: 'Convert temperature between units',
inputSchema: z.object({
fahrenheit: z.number(),
}),
execute: async ({ fahrenheit }) => {
return { celsius: (fahrenheit - 32) * 5/9 };
},
}),
},
});
const result = await weatherAgent.run({
messages: [
{ role: 'user', content: 'What is the weather in SF in Celsius?' },
],
});
console.log(result.text);
// Agent will call getWeather, then convertTemp, then respond
When to Use Agent vs Raw generateText:
- Use Agent when: Multiple tools, complex workflows, multi-step reasoning
- Use generateText when: Simple single-step, one or two tools, full control needed
Multi-Step Execution
Control when multi-step execution stops with stopWhen conditions.
import { generateText, stopWhen, stepCountIs, hasToolCall } from 'ai';
import { openai } from '@ai-sdk/openai';
// Stop after specific number of steps
const result = await generateText({
model: openai('gpt-4'),
tools: { /* ... */ },
prompt: 'Research TypeScript and create a summary',
stopWhen: stepCountIs(5), // Max 5 steps (tool calls + responses)
});
// Stop when specific tool is called
const result = await generateText({
model: openai('gpt-4'),
tools: {
research: tool({ /* ... */ }),
finalize: tool({ /* ... */ }),
},
prompt: 'Research and finalize a report',
stopWhen: hasToolCall('finalize'), // Stop when finalize is called
});
// Combine conditions
const result = await generateText({
model: openai('gpt-4'),
tools: { /* ... */ },
prompt: 'Complex task',
stopWhen: (step) => step.stepCount >= 10 || step.hasToolCall('finish'),
});
v5 Change:
maxSteps parameter removed. Use stopWhen(stepCountIs(n)) instead.
Dynamic Tools (v5 New Feature)
Add tools at runtime based on context:
const result = await generateText({
model: openai('gpt-4'),
tools: (context) => {
// Context includes messages, step count, etc.
const baseTool = {
search: tool({ /* ... */ }),
};
// Add tools based on context
if (context.messages.some(m => m.content.includes('weather'))) {
baseTool.weather = tool({ /* ... */ });
}
return baseTools;
},
prompt: 'Help me with my task',
});
Critical v4→v5 Migration
AI SDK v5 introduced extensive breaking changes. If migrating from v4, follow this guide.
Breaking Changes Overview
Parameter Renames
maxTokens→maxOutputTokensproviderMetadata→providerOptions
Tool Definitions
parameters→inputSchema- Tool properties:
args→input,result→output
Message Types
CoreMessage→ModelMessageMessage→UIMessageconvertToCoreMessages→convertToModelMessages
Tool Error Handling
ToolExecutionErrorclass removed- Now
tool-errorcontent parts - Enables automated retry
Multi-Step Execution
maxSteps→stopWhen- Use
stepCountIs()orhasToolCall()
Message Structure
- Simple
contentstring →partsarray - Parts: text, file, reasoning, tool-call, tool-result
- Simple
Streaming Architecture
- Single chunk → start/delta/end lifecycle
- Unique IDs for concurrent streams
Tool Streaming
- Enabled by default
toolCallStreamingoption removed
Package Reorganization
ai/rsc→@ai-sdk/rscai/react→@ai-sdk/reactLangChainAdapter→@ai-sdk/langchain
Migration Examples
Before (v4):
import { generateText } from 'ai';
const result = await generateText({
model: openai.chat('gpt-4'),
maxTokens: 500,
providerMetadata: { openai: { user: 'user-123' } },
tools: {
weather: {
description: 'Get weather',
parameters: z.object({ location: z.string() }),
execute: async (args) => { /* args.location */ },
},
},
maxSteps: 5,
});
After (v5):
import { generateText, tool, stopWhen, stepCountIs } from 'ai';
const result = await generateText({
model: openai('gpt-4'),
maxOutputTokens: 500,
providerOptions: { openai: { user: 'user-123' } },
tools: {
weather: tool({
description: 'Get weather',
inputSchema: z.object({ location: z.string() }),
execute: async ({ location }) => { /* input.location */ },
}),
},
stopWhen: stepCountIs(5),
});
Migration Checklist
- Update all
maxTokenstomaxOutputTokens - Update
providerMetadatatoproviderOptions - Convert tool
parameterstoinputSchema - Update tool execute functions:
args→input - Replace
maxStepswithstopWhen(stepCountIs(n)) - Update message types:
CoreMessage→ModelMessage - Remove
ToolExecutionErrorhandling - Update package imports (
ai/rsc→@ai-sdk/rsc) - Test streaming behavior (architecture changed)
- Update TypeScript types
Automated Migration
AI SDK provides a migration tool:
npx ai migrate
This will update most breaking changes automatically. Review changes carefully.
Official Migration Guide: https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
Top 12 Errors & Solutions
1. AI_APICallError
Cause: API request failed (network, auth, rate limit).
Solution:
import { AI_APICallError } from 'ai';
try {
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Hello',
});
} catch (error) {
if (error instanceof AI_APICallError) {
console.error('API call failed:', error.message);
console.error('Status code:', error.statusCode);
console.error('Response:', error.responseBody);
// Check common causes
if (error.statusCode === 401) {
// Invalid API key
} else if (error.statusCode === 429) {
// Rate limit - implement backoff
} else if (error.statusCode >= 500) {
// Provider issue - retry
}
}
}
Prevention:
- Validate API keys at startup
- Implement retry logic with exponential backoff
- Monitor rate limits
- Handle network errors gracefully
2. AI_NoObjectGeneratedError
Cause: Model didn't generate valid object matching schema.
Solution:
import { AI_NoObjectGeneratedError } from 'ai';
try {
const result = await generateObject({
model: openai('gpt-4'),
schema: z.object({ /* complex schema */ }),
prompt: 'Generate data',
});
} catch (error) {
if (error instanceof AI_NoObjectGeneratedError) {
console.error('No valid object generated');
// Solutions:
// 1. Simplify schema
// 2. Add more context to prompt
// 3. Provide examples in prompt
// 4. Try different model (gpt-4 better than gpt-3.5 for complex objects)
}
}
Prevention:
- Start with simple schemas, add complexity incrementally
- Include examples in prompt: "Generate a person like: { name: 'Alice', age: 30 }"
- Use GPT-4 for complex structured output
- Test schemas with sample data first
3. Worker Startup Limit (270ms+)
Cause: AI SDK v5 + Zod initialization overhead in Cloudflare Workers exceeds startup limits.
Solution:
// BAD: Top-level imports cause startup overhead
import { createWorkersAI } from 'workers-ai-provider';
import { complexSchema } from './schemas';
const workersai = createWorkersAI({ binding: env.AI });
// GOOD: Lazy initialization inside handler
export default {
async fetch(request, env) {
const { createWorkersAI } = await import('workers-ai-provider');
const workersai = createWorkersAI({ binding: env.AI });
// Use workersai here
}
}
Prevention:
- Move AI SDK imports inside route handlers
- Minimize top-level Zod schemas
- Monitor Worker startup time (must be <400ms)
- Use Wrangler's startup time reporting
GitHub Issue: Search for "Workers startup limit" in Vercel AI SDK issues
4. streamText Fails Silently
Cause: Stream errors can be swallowed by createDataStreamResponse.
Status: ✅ RESOLVED - Fixed in ai@4.1.22 (February 2025)
Solution (Recommended):
// Use the onError callback (added in v4.1.22)
const stream = streamText({
model: openai('gpt-4'),
prompt: 'Hello',
onError({ error }) {
console.error('Stream error:', error);
// Custom error logging and handling
},
});
// Stream safely
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
Alternative (Manual try-catch):
// Fallback if not using onError callback
try {
const stream = streamText({
model: openai('gpt-4'),
prompt: 'Hello',
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
} catch (error) {
console.error('Stream error:', error);
}
Prevention:
- Use
onErrorcallback for proper error capture (recommended) - Implement server-side error monitoring
- Test stream error handling explicitly
- Always log on server side in production
GitHub Issue: #4726 (RESOLVED)
5. AI_LoadAPIKeyError
Cause: Missing or invalid API key.
Solution:
import { AI_LoadAPIKeyError } from 'ai';
try {
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Hello',
});
} catch (error) {
if (error instanceof AI_LoadAPIKeyError) {
console.error('API key error:', error.message);
// Check:
// 1. .env file exists and loaded
// 2. Correct env variable name (OPENAI_API_KEY)
// 3. Key format is valid (starts with sk-)
}
}
Prevention:
- Validate API keys at application startup
- Use environment variable validation (e.g., zod)
- Provide clear error messages in development
- Document required environment variables
6. AI_InvalidArgumentError
Cause: Invalid parameters passed to function.
Solution:
import { AI_InvalidArgumentError } from 'ai';
try {
const result = await generateText({
model: openai('gpt-4'),
maxOutputTokens: -1, // Invalid!
prompt: 'Hello',
});
} catch (error) {
if (error instanceof AI_InvalidArgumentError) {
console.error('Invalid argument:', error.message);
// Check parameter types and values
}
}
Prevention:
- Use TypeScript for type checking
- Validate inputs before calling AI SDK functions
- Read function signatures carefully
- Check official docs for parameter constraints
7. AI_NoContentGeneratedError
Cause: Model generated no content (safety filters, etc.).
Solution:
import { AI_NoContentGeneratedError } from 'ai';
try {
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Some prompt',
});
} catch (error) {
if (error instanceof AI_NoContentGeneratedError) {
console.error('No content generated');
// Possible causes:
// 1. Safety filters blocked output
// 2. Prompt triggered content policy
// 3. Model configuration issue
// Handle gracefully:
return { text: 'Unable to generate response. Please try different input.' };
}
}
Prevention:
- Sanitize user inputs
- Avoid prompts that may trigger safety filters
- Have fallback messaging
- Log occurrences for analysis
8. AI_TypeValidationError
Cause: Zod schema validation failed on generated output.
Solution:
import { AI_TypeValidationError } from 'ai';
try {
const result = await generateObject({
model: openai('gpt-4'),
schema: z.object({
age: z.number().min(0).max(120), // Strict validation
}),
prompt: 'Generate person',
});
} catch (error) {
if (error instanceof AI_TypeValidationError) {
console.error('Validation failed:', error.message);
// Solutions:
// 1. Relax schema constraints
// 2. Add more guidance in prompt
// 3. Use .optional() for unreliable fields
}
}
Prevention:
- Start with lenient schemas, tighten gradually
- Use
.optional()for fields that may not always be present - Add validation hints in field descriptions
- Test with various prompts
9. AI_RetryError
Cause: All retry attempts failed.
Solution:
import { AI_RetryError } from 'ai';
try {
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Hello',
maxRetries: 3, // Default is 2
});
} catch (error) {
if (error instanceof AI_RetryError) {
console.error('All retries failed');
console.error('Last error:', error.lastError);
// Check root cause:
// - Persistent network issue
// - Provider outage
// - Invalid configuration
}
}
Prevention:
- Investigate root cause of failures
- Adjust retry configuration if needed
- Implement circuit breaker pattern for provider outages
- Have fallback providers
10. Rate Limiting Errors
Cause: Exceeded provider rate limits (RPM/TPM).
Solution:
// Implement exponential backoff
async function generateWithBackoff(prompt: string, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await generateText({
model: openai('gpt-4'),
prompt,
});
} catch (error) {
if (error instanceof AI_APICallError && error.statusCode === 429) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
console.log(`Rate limited, waiting ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
throw new Error('Rate limit retries exhausted');
}
Prevention:
- Monitor rate limit headers
- Queue requests to stay under limits
- Upgrade provider tier if needed
- Implement request throttling
11. TypeScript Performance with Zod
Cause: Complex Zod schemas slow down TypeScript type checking.
Solution:
// Instead of deeply nested schemas at top level:
// const complexSchema = z.object({ /* 100+ fields */ });
// Define inside functions or use type assertions:
function generateData() {
const schema = z.object({ /* complex schema */ });
return generateObject({ model: openai('gpt-4'), schema, prompt: '...' });
}
// Or use z.lazy() for recursive schemas:
type Category = { name: string; subcategories?: Category[] };
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
z.object({
name: z.string(),
subcategories: z.array(CategorySchema).optional(),
})
);
Prevention:
- Avoid top-level complex schemas
- Use
z.lazy()for recursive types - Split large schemas into smaller ones
- Use type assertions where appropriate
Official Docs: https://ai-sdk.dev/docs/troubleshooting/common-issues/slow-type-checking
12. Invalid JSON Response (Provider-Specific)
Cause: Some models occasionally return invalid JSON.
Solution:
// Use built-in retry and mode selection
const result = await generateObject({
model: openai('gpt-4'),
schema: mySchema,
prompt: 'Generate data',
mode: 'json', // Force JSON mode (supported by GPT-4)
maxRetries: 3, // Retry on invalid JSON
});
// Or catch and retry manually:
try {
const result = await generateObject({
model: openai('gpt-4'),
schema: mySchema,
prompt: 'Generate data',
});
} catch (error) {
// Retry with different model
const result = await generateObject({
model: openai('gpt-4-turbo'),
schema: mySchema,
prompt: 'Generate data',
});
}
Prevention:
- Use
mode: 'json'when available - Prefer GPT-4 for structured output
- Implement retry logic
- Validate responses
GitHub Issue: #4302 (Imagen 3.0 Invalid JSON)
For More Errors: See complete error reference at https://ai-sdk.dev/docs/reference/ai-sdk-errors
Production Best Practices
Performance
1. Always use streaming for long-form content:
// User-facing: Use streamText
const stream = streamText({ model: openai('gpt-4'), prompt: 'Long essay' });
return stream.toDataStreamResponse();
// Background tasks: Use generateText
const result = await generateText({ model: openai('gpt-4'), prompt: 'Analyze data' });
2. Set appropriate maxOutputTokens:
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Short answer',
maxOutputTokens: 100, // Limit tokens to save cost
});
3. Cache provider instances:
// Good: Reuse provider instances
const gpt4 = openai('gpt-4-turbo');
const result1 = await generateText({ model: gpt4, prompt: 'Hello' });
const result2 = await generateText({ model: gpt4, prompt: 'World' });
4. Optimize Zod schemas:
// Avoid complex nested schemas at top level in Workers
// Move into route handlers to prevent startup overhead
Error Handling
1. Wrap all AI calls in try-catch:
try {
const result = await generateText({ /* ... */ });
} catch (error) {
// Handle specific errors
if (error instanceof AI_APICallError) { /* ... */ }
else if (error instanceof AI_NoContentGeneratedError) { /* ... */ }
else { /* ... */ }
}
2. Implement retry logic:
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Hello',
maxRetries: 3,
});
3. Log errors properly:
console.error('AI SDK Error:', {
type: error.constructor.name,
message: error.message,
statusCode: error.statusCode,
timestamp: new Date().toISOString(),
});
Cost Optimization
1. Choose appropriate models:
// Simple tasks: Use cheaper models
const simple = await generateText({ model: openai('gpt-3.5-turbo'), prompt: 'Hello' });
// Complex reasoning: Use GPT-4
const complex = await generateText({ model: openai('gpt-4'), prompt: 'Analyze...' });
2. Set maxOutputTokens appropriately:
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Summarize in 2 sentences',
maxOutputTokens: 100, // Prevent over-generation
});
3. Cache results when possible:
const cache = new Map();
async function getCachedResponse(prompt: string) {
if (cache.has(prompt)) return cache.get(prompt);
const result = await generateText({ model: openai('gpt-4'), prompt });
cache.set(prompt, result.text);
return result.text;
}
Cloudflare Workers Specific
1. Move imports inside handlers:
// Avoid startup overhead
export default {
async fetch(request, env) {
const { generateText } = await import('ai');
const { openai } = await import('@ai-sdk/openai');
// Use here
}
}
2. Monitor startup time:
# Wrangler reports startup time
wrangler deploy
# Check output for startup duration (must be <400ms)
3. Handle streaming properly:
// Return ReadableStream for streaming responses
const stream = streamText({ model: openai('gpt-4'), prompt: 'Hello' });
return new Response(stream.toTextStream(), {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
Next.js / Vercel Specific
1. Use Server Actions for mutations:
'use server';
export async function generateContent(input: string) {
const result = await generateText({
model: openai('gpt-4'),
prompt: input,
});
return result.text;
}
2. Use Server Components for initial loads:
// app/page.tsx
export default async function Page() {
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Welcome message',
});
return <div>{result.text}</div>;
}
3. Implement loading states:
'use client';
import { useState } from 'react';
import { generateContent } from './actions';
export default function Form() {
const [loading, setLoading] = useState(false);
async function handleSubmit(formData: FormData) {
setLoading(true);
const result = await generateContent(formData.get('input'));
setLoading(false);
}
return (
<form action={handleSubmit}>
<input name="input" />
<button disabled={loading}>
{loading ? 'Generating...' : 'Submit'}
</button>
</form>
);
}
4. For deployment: See Vercel's official deployment documentation: https://vercel.com/docs/functions
When to Use This Skill
Use ai-sdk-core when:
- Building backend AI features (server-side text generation)
- Implementing server-side text generation (Node.js, Workers, Next.js)
- Creating structured AI outputs (JSON, forms, data extraction)
- Building AI agents with tools (multi-step workflows)
- Integrating multiple AI providers (OpenAI, Anthropic, Google, Cloudflare)
- Migrating from AI SDK v4 to v5
- Encountering AI SDK errors (AI_APICallError, AI_NoObjectGeneratedError, etc.)
- Using AI in Cloudflare Workers (with workers-ai-provider)
- Using AI in Next.js Server Components/Actions
- Need consistent API across different LLM providers
Don't use this skill when:
- Building React chat UIs (use ai-sdk-ui skill instead)
- Need frontend hooks like useChat (use ai-sdk-ui skill instead)
- Need advanced topics like embeddings or image generation (check official docs)
- Building native Cloudflare Workers AI apps without multi-provider (use cloudflare-workers-ai skill instead)
- Need Generative UI / RSC (see https://ai-sdk.dev/docs/ai-sdk-rsc)
Dependencies & Versions
{
"dependencies": {
"ai": "^5.0.81",
"@ai-sdk/openai": "^2.0.56",
"@ai-sdk/anthropic": "^2.0.38",
"@ai-sdk/google": "^2.0.24",
"workers-ai-provider": "^2.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20.11.0",
"typescript": "^5.3.3"
}
}
Version Notes:
- AI SDK v5.0.81+ (stable, latest as of October 2025)
- v6 is in beta - not covered in this skill
- Zod compatibility: This skill uses Zod 3.x, but AI SDK 5 officially supports both Zod 3.x and Zod 4.x (4.1.12 latest)
- Zod 4 recommended for new projects (released August 2025)
- Zod 4 has breaking changes: error APIs,
.default()behavior,ZodError.errorsremoved - Some peer dependency warnings may occur with
zod-to-json-schemawhen using Zod 4 - See https://zod.dev/v4/changelog for migration guide
- Provider packages at 2.0+ for v5 compatibility
Check Latest Versions:
npm view ai version
npm view @ai-sdk/openai version
npm view @ai-sdk/anthropic version
npm view @ai-sdk/google version
npm view workers-ai-provider version
npm view zod version # Check for Zod 4.x updates
Links to Official Documentation
Core Documentation
- AI SDK Introduction: https://ai-sdk.dev/docs/introduction
- AI SDK Core Overview: https://ai-sdk.dev/docs/ai-sdk-core/overview
- Generating Text: https://ai-sdk.dev/docs/ai-sdk-core/generating-text
- Generating Structured Data: https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data
- Tools and Tool Calling: https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling
- Agents Overview: https://ai-sdk.dev/docs/agents/overview
- Foundations: https://ai-sdk.dev/docs/foundations/overview
Advanced Topics (Not Replicated in This Skill)
- Embeddings: https://ai-sdk.dev/docs/ai-sdk-core/embeddings
- Image Generation: https://ai-sdk.dev/docs/ai-sdk-core/generating-images
- Transcription: https://ai-sdk.dev/docs/ai-sdk-core/generating-transcriptions
- Speech: https://ai-sdk.dev/docs/ai-sdk-core/generating-speech
- MCP Tools: https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools
- Telemetry: https://ai-sdk.dev/docs/ai-sdk-core/telemetry
- Generative UI: https://ai-sdk.dev/docs/ai-sdk-rsc
Migration & Troubleshooting
- v4→v5 Migration Guide: https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
- All Error Types (28 total): https://ai-sdk.dev/docs/reference/ai-sdk-errors
- Troubleshooting Guide: https://ai-sdk.dev/docs/troubleshooting
Provider Documentation
- OpenAI Provider: https://ai-sdk.dev/providers/ai-sdk-providers/openai
- Anthropic Provider: https://ai-sdk.dev/providers/ai-sdk-providers/anthropic
- Google Provider: https://ai-sdk.dev/providers/ai-sdk-providers/google
- All Providers (25+): https://ai-sdk.dev/providers/overview
- Community Providers: https://ai-sdk.dev/providers/community-providers
Cloudflare Integration
- Workers AI Provider (Community): https://ai-sdk.dev/providers/community-providers/cloudflare-workers-ai
- Cloudflare Workers AI Docs: https://developers.cloudflare.com/workers-ai/
- workers-ai-provider GitHub: https://github.com/cloudflare/ai/tree/main/packages/workers-ai-provider
- Cloudflare AI SDK Configuration: https://developers.cloudflare.com/workers-ai/configuration/ai-sdk/
Vercel / Next.js Integration
- Vercel AI SDK 5.0 Blog: https://vercel.com/blog/ai-sdk-5
- Next.js App Router Integration: https://ai-sdk.dev/docs/getting-started/nextjs-app-router
- Next.js Pages Router Integration: https://ai-sdk.dev/docs/getting-started/nextjs-pages-router
- Vercel Functions: https://vercel.com/docs/functions
- Vercel Streaming: https://vercel.com/docs/functions/streaming
GitHub & Community
- GitHub Repository: https://github.com/vercel/ai
- GitHub Issues: https://github.com/vercel/ai/issues
- Discord Community: https://discord.gg/vercel
Templates & References
This skill includes:
- 13 Templates: Ready-to-use code examples in
templates/ - 5 Reference Docs: Detailed guides in
references/ - 1 Script: Version checker in
scripts/
All files are optimized for copy-paste into your project.
Last Updated: 2025-10-29 Skill Version: 1.1.0 AI SDK Version: 5.0.81+