New skills (14): - nestjs-best-practices: 40 priority-ranked rules (kadajett) - fastapi: Pydantic v2, async SQLAlchemy, JWT auth (jezweb) - architecture-patterns: Clean Architecture, Hexagonal, DDD (wshobson) - python-performance-optimization: Profiling and optimization (wshobson) - ai-sdk: Vercel AI SDK streaming and agent patterns (vercel) - create-agent: Modular agent architecture with OpenRouter (openrouterteam) - proactive-agent: WAL Protocol, compaction recovery, self-improvement (halthelobster) - brand-guidelines: Brand identity enforcement (anthropics) - ui-animation: Motion design with accessibility (mblode) - marketing-ideas: 139 ideas across 14 categories (coreyhaines31) - pricing-strategy: SaaS pricing and tier design (coreyhaines31) - programmatic-seo: SEO at scale with playbooks (coreyhaines31) - competitor-alternatives: Comparison page architecture (coreyhaines31) - referral-program: Referral and affiliate programs (coreyhaines31) README reorganized by domain: Code Quality, Frontend, Backend, Auth, AI/Agent Building, Marketing, Design, Meta. Mosaic Stack is not limited to coding — the Orchestrator serves coding, business, design, marketing, writing, logistics, and analysis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 KiB
title, description
| title | description |
|---|---|
| Common Errors | Reference for common AI SDK errors and how to resolve them. |
Common Errors
maxTokens → maxOutputTokens
// ❌ Incorrect
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
maxTokens: 512, // deprecated: use `maxOutputTokens` instead
prompt: 'Write a short story',
});
// ✅ Correct
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
maxOutputTokens: 512,
prompt: 'Write a short story',
});
maxSteps → stopWhen: stepCountIs(n)
// ❌ Incorrect
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
tools: { weather },
maxSteps: 5, // deprecated: use `stopWhen: stepCountIs(n)` instead
prompt: 'What is the weather in NYC?',
});
// ✅ Correct
import { generateText, stepCountIs } from 'ai';
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
tools: { weather },
stopWhen: stepCountIs(5),
prompt: 'What is the weather in NYC?',
});
parameters → inputSchema (in tool definition)
// ❌ Incorrect
const weatherTool = tool({
description: 'Get weather for a location',
parameters: z.object({
// deprecated: use `inputSchema` instead
location: z.string(),
}),
execute: async ({ location }) => ({ location, temp: 72 }),
});
// ✅ Correct
const weatherTool = tool({
description: 'Get weather for a location',
inputSchema: z.object({
location: z.string(),
}),
execute: async ({ location }) => ({ location, temp: 72 }),
});
generateObject → generateText with output
generateObject is deprecated. Use generateText with the output option instead.
// ❌ Deprecated
import { generateObject } from 'ai'; // deprecated: use `generateText` with `output` instead
const result = await generateObject({
// deprecated function
model: 'anthropic/claude-opus-4.5',
schema: z.object({
// deprecated: use `Output.object({ schema })` instead
recipe: z.object({
name: z.string(),
ingredients: z.array(z.string()),
}),
}),
prompt: 'Generate a recipe for chocolate cake',
});
// ✅ Correct
import { generateText, Output } from 'ai';
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
output: Output.object({
schema: z.object({
recipe: z.object({
name: z.string(),
ingredients: z.array(z.string()),
}),
}),
}),
prompt: 'Generate a recipe for chocolate cake',
});
console.log(result.output); // typed object
Manual JSON parsing → generateText with output
// ❌ Incorrect
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
prompt: `Extract the user info as JSON: { "name": string, "age": number }
Input: John is 25 years old`,
});
const parsed = JSON.parse(result.text);
// ✅ Correct
import { generateText, Output } from 'ai';
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
output: Output.object({
schema: z.object({
name: z.string(),
age: z.number(),
}),
}),
prompt: 'Extract the user info: John is 25 years old',
});
console.log(result.output); // { name: 'John', age: 25 }
Other output options
// Output.array - for generating arrays of items
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
output: Output.array({
element: z.object({
city: z.string(),
country: z.string(),
}),
}),
prompt: 'List 5 capital cities',
});
// Output.choice - for selecting from predefined options
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
output: Output.choice({
options: ['positive', 'negative', 'neutral'] as const,
}),
prompt: 'Classify the sentiment: I love this product!',
});
// Output.json - for untyped JSON output
const result = await generateText({
model: 'anthropic/claude-opus-4.5',
output: Output.json(),
prompt: 'Return some JSON data',
});
toDataStreamResponse → toUIMessageStreamResponse
When using useChat on the frontend, use toUIMessageStreamResponse() instead of toDataStreamResponse(). The UI message stream format is designed to work with the chat UI components and handles message state correctly.
// ❌ Incorrect (when using useChat)
const result = streamText({
// config
});
return result.toDataStreamResponse(); // deprecated for useChat: use toUIMessageStreamResponse
// ✅ Correct
const result = streamText({
// config
});
return result.toUIMessageStreamResponse();
Removed managed input state in useChat
The useChat hook no longer manages input state internally. You must now manage input state manually.
// ❌ Deprecated
import { useChat } from '@ai-sdk/react';
export default function Page() {
const {
input, // deprecated: manage input state manually with useState
handleInputChange, // deprecated: use custom onChange handler
handleSubmit, // deprecated: use sendMessage() instead
} = useChat({
api: '/api/chat', // deprecated: use `transport: new DefaultChatTransport({ api })` instead
});
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">Send</button>
</form>
);
}
// ✅ Correct
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { useState } from 'react';
export default function Page() {
const [input, setInput] = useState('');
const { sendMessage } = useChat({
transport: new DefaultChatTransport({ api: '/api/chat' }),
});
const handleSubmit = e => {
e.preventDefault();
sendMessage({ text: input });
setInput('');
};
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={e => setInput(e.target.value)} />
<button type="submit">Send</button>
</form>
);
}
tool-invocation → tool-{toolName} (typed tool parts)
When rendering messages with useChat, use the typed tool part names (tool-{toolName}) instead of the generic tool-invocation type. This provides better type safety and access to tool-specific input/output types.
For end-to-end type-safety, see Type-Safe Agents.
Typed tool parts also use different property names:
part.args→part.inputpart.result→part.output
// ❌ Incorrect - using generic tool-invocation
{
message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <div key={`${message.id}-${i}`}>{part.text}</div>;
case 'tool-invocation': // deprecated: use typed tool parts instead
return (
<pre key={`${message.id}-${i}`}>
{JSON.stringify(part.toolInvocation, null, 2)}
</pre>
);
}
});
}
// ✅ Correct - using typed tool parts (recommended)
{
message.parts.map(part => {
switch (part.type) {
case 'text':
return part.text;
case 'tool-askForConfirmation':
// handle askForConfirmation tool
break;
case 'tool-getWeatherInformation':
// handle getWeatherInformation tool
break;
}
});
}
// ✅ Alternative - using isToolUIPart as a catch-all
import { isToolUIPart } from 'ai';
{
message.parts.map(part => {
if (part.type === 'text') {
return part.text;
}
if (isToolUIPart(part)) {
// handle any tool part generically
return (
<div key={part.toolCallId}>
{part.toolName}: {part.state}
</div>
);
}
});
}
useChat state-dependent property access
Tool part properties are only available in certain states. TypeScript will error if you access them without checking state first.
// ❌ Incorrect - input may be undefined during streaming
// TS18048: 'part.input' is possibly 'undefined'
if (part.type === 'tool-getWeather') {
const location = part.input.location;
}
// ✅ Correct - check for input-available or output-available
if (
part.type === 'tool-getWeather' &&
(part.state === 'input-available' || part.state === 'output-available')
) {
const location = part.input.location;
}
// ❌ Incorrect - output is only available after execution
// TS18048: 'part.output' is possibly 'undefined'
if (part.type === 'tool-getWeather') {
const weather = part.output;
}
// ✅ Correct - check for output-available
if (part.type === 'tool-getWeather' && part.state === 'output-available') {
const location = part.input.location;
const weather = part.output;
}
part.toolInvocation.args → part.input
// ❌ Incorrect
if (part.type === 'tool-invocation') {
// deprecated: use `part.input` on typed tool parts instead
const location = part.toolInvocation.args.location;
}
// ✅ Correct
if (
part.type === 'tool-getWeather' &&
(part.state === 'input-available' || part.state === 'output-available')
) {
const location = part.input.location;
}
part.toolInvocation.result → part.output
// ❌ Incorrect
if (part.type === 'tool-invocation') {
// deprecated: use `part.output` on typed tool parts instead
const weather = part.toolInvocation.result;
}
// ✅ Correct
if (part.type === 'tool-getWeather' && part.state === 'output-available') {
const weather = part.output;
}
part.toolInvocation.toolCallId → part.toolCallId
// ❌ Incorrect
if (part.type === 'tool-invocation') {
// deprecated: use `part.toolCallId` on typed tool parts instead
const id = part.toolInvocation.toolCallId;
}
// ✅ Correct
if (part.type === 'tool-getWeather') {
const id = part.toolCallId;
}
Tool invocation states renamed
// ❌ Incorrect
switch (part.toolInvocation.state) {
case 'partial-call': // deprecated: use `input-streaming` instead
return <div>Loading...</div>;
case 'call': // deprecated: use `input-available` instead
return <div>Executing...</div>;
case 'result': // deprecated: use `output-available` instead
return <div>Done</div>;
}
// ✅ Correct
switch (part.state) {
case 'input-streaming':
return <div>Loading...</div>;
case 'input-available':
return <div>Executing...</div>;
case 'output-available':
return <div>Done</div>;
}
addToolResult → addToolOutput
// ❌ Incorrect
addToolResult({
// deprecated: use `addToolOutput` instead
toolCallId: part.toolInvocation.toolCallId,
result: 'Yes, confirmed.', // deprecated: use `output` instead
});
// ✅ Correct
addToolOutput({
tool: 'askForConfirmation',
toolCallId: part.toolCallId,
output: 'Yes, confirmed.',
});
messages → uiMessages in createAgentUIStreamResponse
// ❌ Incorrect
return createAgentUIStreamResponse({
agent: myAgent,
messages, // incorrect: use `uiMessages` instead
});
// ✅ Correct
return createAgentUIStreamResponse({
agent: myAgent,
uiMessages: messages,
});