{"record":{"id":"a6bed8229248bcd8","repo":"can1357/oh-my-pi","slug":"received-thinking-delta-for-non-thinking-content","errorCode":null,"errorMessage":"Received thinking_delta for non-thinking content","messagePattern":"Received thinking_delta for non-thinking content","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/proxy.ts","lineNumber":305,"sourceCode":"\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: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_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 thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"image_end\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = proxyEvent.content;","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/agent/src/proxy.ts#L287-L323","documentation":"thinking_delta requires the content slot at contentIndex to be a thinking block created by a preceding thinking_start. If the slot is missing or holds text/toolCall/image, the stream violates the proxy event contract and the library throws to protect message integrity.","triggerScenarios":"thinking_delta arrives without a prior thinking_start at that contentIndex; thinking_start was overwritten by another block; provider adapter emits thinking deltas under the wrong index.","commonSituations":"Models with extended thinking via a custom/relay provider whose adapter misindexes reasoning chunks; dropping thinking_start when filtering reasoning-only events; a provider that interleaves reasoning and text on shared indexes.","solutions":["Emit thinking_start at the exact contentIndex before any thinking_delta","Fix the adapter's index mapping for reasoning blocks (each block type must own its own index)","Do not drop thinking_start events when filtering the stream","Update proxy/agent packages to compatible versions"],"exampleFix":"// before\nfor (const chunk of reasoningChunks) yield { type: \"thinking_delta\", contentIndex: i, delta: chunk }\n// after\nyield { type: \"thinking_start\", contentIndex: i }\nfor (const chunk of reasoningChunks) yield { type: \"thinking_delta\", contentIndex: i, delta: chunk }","handlingStrategy":"type-guard","validationCode":"if (partial.content[ev.contentIndex]?.type !== \"thinking\") {\n  throw new Error(`thinking_delta at ${ev.contentIndex} without a thinking block`);\n}","typeGuard":"function isThinkingSlot(partial: AssistantMessage, i: number): boolean {\n  return partial.content[i]?.type === \"thinking\";\n}","tryCatchPattern":"try {\n  processProxyEvent(model, ev, partial, partialJsonByIndex);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"thinking_delta for non-thinking\")) {\n    logger.warn(\"Dropping orphan thinking_delta\", { contentIndex: ev.contentIndex });\n    return;\n  }\n  throw err;\n}","preventionTips":["Emit thinking_start before any thinking_delta at the same index","Give thinking blocks their own contentIndex separate from text/toolCall","Don't strip thinking_start when hiding reasoning from the UI — filter downstream instead","Verify adapters against extended-thinking models"],"tags":["streaming","protocol","thinking"],"backgroundTag":"stream-out-of-order","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}