vercel/ai · error · UIMessageStreamError
Received text-delta for missing text part with ID "${chunk.i
Error message
Received text-delta for missing text part with ID "${chunk.id}". Ensure a "text-start" chunk is sent before any "text-delta" chunks. What it means
The UI message stream processor tracks open text parts in `state.activeTextParts` keyed by chunk id. A 'text-delta' chunk arrived whose id has no active text part, meaning no 'text-start' chunk opened it. The processor throws UIMessageStreamError because appending a delta to a nonexistent part would lose or corrupt text.
Source
Thrown at packages/ai/src/ui/process-ui-message-stream.ts:443
switch (chunk.type) {
case 'text-start': {
const textPart: TextUIPart = {
type: 'text',
text: '',
providerMetadata: chunk.providerMetadata,
state: 'streaming',
};
state.activeTextParts[chunk.id] = textPart;
state.message.parts.push(textPart);
write();
break;
}
case 'text-delta': {
const textPart = state.activeTextParts[chunk.id];
if (textPart == null) {
throw new UIMessageStreamError({
chunkType: 'text-delta',
chunkId: chunk.id,
message:
`Received text-delta for missing text part with ID "${chunk.id}". ` +
`Ensure a "text-start" chunk is sent before any "text-delta" chunks.`,
});
}
textPart.text += chunk.delta;
textPart.providerMetadata =
chunk.providerMetadata ?? textPart.providerMetadata;
write();
break;
}
case 'text-end': {
const textPart = state.activeTextParts[chunk.id];
if (textPart == null) {
throw new UIMessageStreamError({View on GitHub (pinned to 69428b1f8b)
Solutions
- Always send a 'text-start' chunk with the same id before any 'text-delta' chunks.
- Ensure the id on every text-delta matches an id from a currently open text-start (not yet text-end).
- If resuming mid-stream, restart the text part with a fresh text-start before continuing deltas.
- Catch UIMessageStreamError and log/skip orphan deltas if the stream is best-effort.
Example fix
// before
writer.write({ type: 'text-delta', id: 't1', delta: 'Hello' });
// after
writer.write({ type: 'text-start', id: 't1' });
writer.write({ type: 'text-delta', id: 't1', delta: 'Hello' });
writer.write({ type: 'text-end', id: 't1' }); Defensive patterns
Strategy: validation
Validate before calling
const openIds = new Set<string>();
function assertTextOpen(id: string) {
if (!openIds.has(id)) throw new Error(`text-delta without text-start for id ${id}`);
} Try / catch
try {
await processUIMessageStream(...);
} catch (e) {
if (e instanceof Error && /text-delta for missing text part/.test(e.message)) {
console.warn('Malformed stream: orphan text-delta', e.message);
} else throw e;
} Prevention
- Use the SDK's stream writer (createUIMessageStream writer) rather than emitting raw chunks.
- Emit matching start/delta/end triples for every text part id.
- Track open part ids server-side and assert lifecycle order in tests.
- Include start chunks when resuming/replaying persisted streams.
When it happens
Trigger: A stream containing 'text-delta' with a given id where the preceding 'text-start' chunk was never emitted (or was already closed by 'text-end'), e.g. in custom stream writers or hand-built SSE streams.
Common situations: Custom backend stream implementations forgetting text-start; id mismatch between text-start and text-delta; replaying/resuming a stream after text-start was already consumed; reusing the same text part id after text-end.
Related errors
- Received text-end for missing text part with ID "${chunk.id}
- Received reasoning-delta for missing reasoning part with ID
- Received reasoning-end for missing reasoning part with ID "$
- Received tool-input-delta for missing tool call with ID "${c
- No tool invocation found for tool call ID "${toolCallId}".
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/3bc5e1f3efe093ec.
Report an issue: GitHub.