{"record":{"id":"3bc5e1f3efe093ec","repo":"vercel/ai","slug":"received-text-delta-for-missing-text-part-with-id","errorCode":null,"errorMessage":"Received text-delta for missing text part with ID \"${chunk.id}\". Ensure a \"text-start\" chunk is sent before any \"text-delta\" chunks.","messagePattern":"Received text-delta for missing text part with ID \"(.+?)\"\\. Ensure a \"text-start\" chunk is sent before any \"text-delta\" chunks\\.","errorType":"exception","errorClass":"UIMessageStreamError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/ui/process-ui-message-stream.ts","lineNumber":443,"sourceCode":"\n          switch (chunk.type) {\n            case 'text-start': {\n              const textPart: TextUIPart = {\n                type: 'text',\n                text: '',\n                providerMetadata: chunk.providerMetadata,\n                state: 'streaming',\n              };\n              state.activeTextParts[chunk.id] = textPart;\n              state.message.parts.push(textPart);\n              write();\n              break;\n            }\n\n            case 'text-delta': {\n              const textPart = state.activeTextParts[chunk.id];\n              if (textPart == null) {\n                throw new UIMessageStreamError({\n                  chunkType: 'text-delta',\n                  chunkId: chunk.id,\n                  message:\n                    `Received text-delta for missing text part with ID \"${chunk.id}\". ` +\n                    `Ensure a \"text-start\" chunk is sent before any \"text-delta\" chunks.`,\n                });\n              }\n              textPart.text += chunk.delta;\n              textPart.providerMetadata =\n                chunk.providerMetadata ?? textPart.providerMetadata;\n              write();\n              break;\n            }\n\n            case 'text-end': {\n              const textPart = state.activeTextParts[chunk.id];\n              if (textPart == null) {\n                throw new UIMessageStreamError({","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/ui/process-ui-message-stream.ts#L425-L461","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nwriter.write({ type: 'text-delta', id: 't1', delta: 'Hello' });\n// after\nwriter.write({ type: 'text-start', id: 't1' });\nwriter.write({ type: 'text-delta', id: 't1', delta: 'Hello' });\nwriter.write({ type: 'text-end', id: 't1' });","handlingStrategy":"validation","validationCode":"const openIds = new Set<string>();\nfunction assertTextOpen(id: string) {\n  if (!openIds.has(id)) throw new Error(`text-delta without text-start for id ${id}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await processUIMessageStream(...);\n} catch (e) {\n  if (e instanceof Error && /text-delta for missing text part/.test(e.message)) {\n    console.warn('Malformed stream: orphan text-delta', e.message);\n  } else throw e;\n}","preventionTips":["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."],"tags":["streaming","ui-message-stream","protocol-violation","text-parts"],"backgroundTag":"missing-stream-chunk","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}