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

  1. Emit a 'reasoning-start' chunk with the same id before any 'reasoning-delta'.
  2. Confirm reasoning part ids are consistent across start/delta/end.
  3. On resume, re-open the reasoning part with reasoning-start before continuing deltas.
  4. 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

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


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/2ec8bc1dc5bfe63b. Report an issue: GitHub.