vercel/ai · error · UIMessageStreamError
Received reasoning-delta for missing reasoning part with ID
Error message
Received reasoning-delta for missing reasoning part with ID "${chunk.id}". Ensure a "reasoning-start" chunk is sent before any "reasoning-delta" chunks. What it means
Reasoning parts work like text parts: 'reasoning-start' registers the part in `state.activeReasoningParts` by id. A 'reasoning-delta' whose id has no active reasoning part triggers this UIMessageStreamError. The protocol requires reasoning-start before any deltas.
Source
Thrown at packages/ai/src/ui/process-ui-message-stream.ts:505
case 'reasoning-start': {
const reasoningPart: ReasoningUIPart = {
type: 'reasoning',
id: chunk.id,
text: '',
providerMetadata: chunk.providerMetadata,
state: 'streaming',
};
state.activeReasoningParts[chunk.id] = reasoningPart;
state.message.parts.push(reasoningPart);
write();
break;
}
case 'reasoning-delta': {
const reasoningPart = state.activeReasoningParts[chunk.id];
if (reasoningPart == null) {
throw new UIMessageStreamError({
chunkType: 'reasoning-delta',
chunkId: chunk.id,
message:
`Received reasoning-delta for missing reasoning part with ID "${chunk.id}". ` +
`Ensure a "reasoning-start" chunk is sent before any "reasoning-delta" chunks.`,
});
}
reasoningPart.text += chunk.delta;
reasoningPart.providerMetadata =
chunk.providerMetadata ?? reasoningPart.providerMetadata;
write();
break;
}
case 'reasoning-end': {
const reasoningPart = state.activeReasoningParts[chunk.id];
if (reasoningPart == null) {
throw new UIMessageStreamError({View on GitHub (pinned to 69428b1f8b)
Solutions
- Emit a 'reasoning-start' chunk with the same id before any 'reasoning-delta'.
- Confirm reasoning part ids are consistent across start/delta/end.
- On resume, re-open the reasoning part with reasoning-start before continuing deltas.
- Catch UIMessageStreamError and skip orphan reasoning deltas when processing best-effort streams.
Example fix
// before
writer.write({ type: 'reasoning-delta', id: 'r1', delta: 'thinking...' });
// after
writer.write({ type: 'reasoning-start', id: 'r1' });
writer.write({ type: 'reasoning-delta', id: 'r1', delta: 'thinking...' });
writer.write({ type: 'reasoning-end', id: 'r1' }); Defensive patterns
Strategy: validation
Validate before calling
const openReasoning = new Set<string>();
function assertReasoningOpen(id: string) {
if (!openReasoning.has(id)) throw new Error(`reasoning-delta without reasoning-start for id ${id}`);
} Try / catch
try {
await processUIMessageStream(...);
} catch (e) {
if (e instanceof Error && /reasoning-delta for missing reasoning part/.test(e.message)) {
console.warn('Malformed stream: orphan reasoning-delta', e.message);
} else throw e;
} Prevention
- Always open reasoning parts with reasoning-start before streaming deltas.
- Keep reasoning part ids consistent across start/delta/end.
- On resume, re-open reasoning parts before continuing deltas.
- Test custom provider bridges with snapshot tests of the chunk sequence.
When it happens
Trigger: A UI message stream emits 'reasoning-delta' with an id that never received 'reasoning-start' (or whose part was already closed by 'reasoning-end') — custom writers, resumed streams, or id mismatches.
Common situations: Custom provider bridges streaming reasoning without the start chunk; mismatched reasoning part ids between start and delta; replaying a persisted stream from mid-point; streaming libraries upgraded where the id scheme changed.
Related errors
- Received reasoning-end for missing reasoning part with ID "$
- Received text-delta for missing text part with ID "${chunk.i
- Received text-end for missing text part with ID "${chunk.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/2ec8bc1dc5bfe63b.
Report an issue: GitHub.