{"record":{"id":"961e8a62c0378cd7","repo":"can1357/oh-my-pi","slug":"received-text-delta-for-non-text-content","errorCode":null,"errorMessage":"Received text_delta for non-text content","messagePattern":"Received text_delta for non-text content","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/proxy.ts","lineNumber":273,"sourceCode":"\t\t\t(partial as { stopReason?: string }).stopReason = undefined;\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/agent/src/proxy.ts#L255-L291","documentation":"processProxyEvent folds streamed proxy events into an in-progress AssistantMessage. A text_delta must land on a content slot already initialized as a text block by a preceding text_start at the same contentIndex. When the slot is missing, empty (hole), or holds a different block type (thinking/toolCall/image), the proxy protocol is out of order or inconsistent, so the library throws rather than corrupting the message.","triggerScenarios":"An upstream/provider event stream emits text_delta with a contentIndex that never got a text_start, or a text_start at index N was overwritten by another block type at index N, or events were dropped/reordered between the proxy and processProxyEvent.","commonSituations":"A custom or third-party API adapter mis-translating native chunks into proxy events; skipping text_start while replaying or filtering events; concurrent writes to the same contentIndex from interleaved streams; consuming events from a snapshot of the stream after a retry re-numbered content blocks.","solutions":["Ensure every text_delta is preceded by a text_start with the identical contentIndex","Check the provider adapter/translator so native delta events map to the same contentIndex as their start event","Stop filtering, deduplicating, or reordering proxy events before passing them to processProxyEvent","Upgrade the proxy/agent packages to matching versions so event shapes agree"],"exampleFix":"// before: emitting deltas without a start\nyield { type: \"text_delta\", contentIndex: 0, delta: chunk }\n// after: always start the block first\nyield { type: \"text_start\", contentIndex: 0 }\nyield { type: \"text_delta\", contentIndex: 0, delta: chunk }","handlingStrategy":"type-guard","validationCode":"const slot = partial.content[ev.contentIndex];\nif (slot?.type !== \"text\") throw new Error(`text_delta at index ${ev.contentIndex} has no text block (got ${slot?.type ?? \"empty\"})`);","typeGuard":"function hasTextBlock(partial: AssistantMessage, i: number): boolean {\n  return partial.content[i]?.type === \"text\";\n}","tryCatchPattern":"try {\n  const ev2 = processProxyEvent(model, ev, partial, partialJsonByIndex);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"text_delta for non-text\")) {\n    logger.warn(\"Dropping misordered text_delta\", { contentIndex: ev.contentIndex });\n    return;\n  }\n  throw err;\n}","preventionTips":["Always emit text_start before text_delta at the same contentIndex","Never filter start events out of a proxy event stream","Keep producer and consumer of proxy events on the same package version","Log contentIndex and existing block type when the stream misbehaves"],"tags":["streaming","protocol","proxy"],"backgroundTag":"stream-out-of-order","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}