{"record":{"id":"451ec7428798c211","repo":"vercel/ai","slug":"received-reasoning-end-for-missing-reasoning-part","errorCode":null,"errorMessage":"Received reasoning-end for missing reasoning part with ID \"${chunk.id}\". Ensure a \"reasoning-start\" chunk is sent before any \"reasoning-end\" chunks.","messagePattern":"Received reasoning-end for missing reasoning part with ID \"(.+?)\"\\. Ensure a \"reasoning-start\" chunk is sent before any \"reasoning-end\" chunks\\.","errorType":"exception","errorClass":"UIMessageStreamError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/ui/process-ui-message-stream.ts","lineNumber":523,"sourceCode":"                throw new UIMessageStreamError({\n                  chunkType: 'reasoning-delta',\n                  chunkId: chunk.id,\n                  message:\n                    `Received reasoning-delta for missing reasoning part with ID \"${chunk.id}\". ` +\n                    `Ensure a \"reasoning-start\" chunk is sent before any \"reasoning-delta\" chunks.`,\n                });\n              }\n              reasoningPart.text += chunk.delta;\n              reasoningPart.providerMetadata =\n                chunk.providerMetadata ?? reasoningPart.providerMetadata;\n              write();\n              break;\n            }\n\n            case 'reasoning-end': {\n              const reasoningPart = state.activeReasoningParts[chunk.id];\n              if (reasoningPart == null) {\n                throw new UIMessageStreamError({\n                  chunkType: 'reasoning-end',\n                  chunkId: chunk.id,\n                  message:\n                    `Received reasoning-end for missing reasoning part with ID \"${chunk.id}\". ` +\n                    `Ensure a \"reasoning-start\" chunk is sent before any \"reasoning-end\" chunks.`,\n                });\n              }\n              reasoningPart.providerMetadata =\n                chunk.providerMetadata ?? reasoningPart.providerMetadata;\n              reasoningPart.state = 'done';\n              delete state.activeReasoningParts[chunk.id];\n\n              write();\n              break;\n            }\n\n            case 'file':\n            case 'reasoning-file': {","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/ui/process-ui-message-stream.ts#L505-L541","documentation":"When handling 'reasoning-end', the processor requires an active reasoning part with the chunk id in `state.activeReasoningParts`. If none exists (never started or already ended), it throws this UIMessageStreamError. Reasoning parts must be opened before they can be closed.","triggerScenarios":"A 'reasoning-end' chunk arrives with an id lacking a matching open 'reasoning-start' — duplicate ends, id mismatch, or out-of-order chunks in custom streams.","commonSituations":"Server-side code emitting reasoning-end twice; reasoning start/end ids diverging after refactors; stream replays that skip the start chunk; third-party provider adapters generating malformed chunk sequences.","solutions":["Emit 'reasoning-start' before 'reasoning-end' with identical ids, exactly once each.","Track open reasoning part ids on the server to avoid double ends.","Ensure stream persistence/replay includes the start chunk or restarts parts on resume.","Catch UIMessageStreamError for reasoning-end and treat as a no-op for already-closed parts."],"exampleFix":"// before\nwriter.write({ type: 'reasoning-end', id: 'r1' }); // never started\n// after\nwriter.write({ type: 'reasoning-start', id: 'r1' });\nwriter.write({ type: 'reasoning-delta', id: 'r1', delta: '...' });\nwriter.write({ type: 'reasoning-end', id: 'r1' });","handlingStrategy":"validation","validationCode":"const openReasoning = new Set<string>();\nfunction assertReasoningOpenBeforeEnd(id: string) {\n  if (!openReasoning.has(id)) throw new Error(`reasoning-end without open reasoning-start for id ${id}`);\n  openReasoning.delete(id);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await processUIMessageStream(...);\n} catch (e) {\n  if (e instanceof Error && /reasoning-end for missing reasoning part/.test(e.message)) {\n    console.warn('Malformed stream: orphan reasoning-end', e.message);\n  } else throw e;\n}","preventionTips":["Emit reasoning-end exactly once per reasoning part and only after reasoning-start.","Track open reasoning parts server-side to prevent duplicate end chunks.","Use shared id variables across start/delta/end emissions.","Validate chunk ordering in integration tests for custom streams."],"tags":["streaming","ui-message-stream","reasoning","protocol-violation"],"backgroundTag":"missing-stream-chunk","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}