{"record":{"id":"b079258c771f2bde","repo":"spring-projects/spring-ai","slug":"thinking-block-completed-without-a-signature-ski","errorCode":null,"errorMessage":"Thinking block completed without a signature — skipping replay capture","messagePattern":"Thinking block completed without a signature — skipping replay capture","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java","lineNumber":1719,"sourceCode":"\t\t\tthis.currentThinking.set(true);\n\t\t\tthis.currentThinkingAccumulator.setLength(0);\n\t\t\tthis.currentThinkingSignature.set(null);\n\t\t}\n\n\t\tvoid appendThinking(String thinking) {\n\t\t\tthis.currentThinkingAccumulator.append(thinking);\n\t\t}\n\n\t\tvoid setThinkingSignature(String signature) {\n\t\t\tthis.currentThinkingSignature.set(signature);\n\t\t}\n\n\t\tvoid finishThinking() {\n\t\t\tif (this.currentThinking.get()) {\n\t\t\t\tString signature = this.currentThinkingSignature.get();\n\t\t\t\tif (signature == null) {\n\t\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\t\tlogger.warn(\"Thinking block completed without a signature — skipping replay capture\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.thinkingContents\n\t\t\t\t\t\t.add(AnthropicThinkingContent.thinking(this.currentThinkingAccumulator.toString(), signature));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.currentThinking.set(false);\n\t\t\tthis.currentThinkingAccumulator.setLength(0);\n\t\t\tthis.currentThinkingSignature.set(null);\n\t\t}\n\n\t\tvoid addThinkingContent(AnthropicThinkingContent thinkingContent) {\n\t\t\tthis.thinkingContents.add(thinkingContent);\n\t\t}\n\n\t\t/**\n\t\t * Appends partial JSON to the current tool's input accumulator.","sourceCodeStart":1701,"sourceCodeEnd":1737,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java#L1701-L1737","documentation":"A warning emitted during streaming when a thinking block finishes but Anthropic never delivered a signature field. Without a signature the thinking content cannot be replayed in a subsequent request (Anthropic requires signed thinking for extended-thinking multi-turn), so it is skipped from thinkingContents. Responses relying on replayed thinking will be missing that block.","triggerScenarios":"Streaming responses with extended thinking enabled where the message_delta/stop event arrives with an empty or absent signature_delta for the current thinking block — e.g. redacted thinking, API behavior changes, or truncated streams.","commonSituations":"Using streaming with thinking enabled and a proxy that drops signature deltas; multi-turn conversations where the previous assistant turn's thinking had no signature; SDK/model version mismatch where signature events are named differently.","solutions":["Upgrade spring-ai-anthropic / anthropic-java SDK to a version that captures signatures from the correct streaming events","Check that no proxy or interceptor strips signature_delta events from the stream","Disable thinking replay expectations: if you do not need to send thinking back, ignore the warning","Verify the model actually supports/returns signed thinking (not redacted thinking blocks)","Test with non-streaming call to confirm signatures are present in the blocking response"],"exampleFix":"// before\n// relying on streamed thinking to be replayed\nChatResponse resp = chatModel.call(new Prompt(prompt, opts)); // warning: no signature\n// after\n// verify stream integrity or capture signature explicitly\nclient.messages().stream(...).forEach(event -> {\n    if (event.signatureDelta() != null) {\n        signatureBuilder.append(event.signatureDelta().signature());\n    }\n});","handlingStrategy":"retry","validationCode":null,"typeGuard":"boolean hasValidThinking(Generation g) {\n    return g.getOutput().getMetadata().containsKey(\"thinking\")\n        && g.getOutput().getMetadata().get(\"signature\") != null;\n}","tryCatchPattern":"// streaming path: detect missing signatures and retry non-streaming\nif (sawThinkingWithoutSignature) {\n    logger.warn(\"Signature missing in stream; falling back to blocking call\");\n    response = chatModel.call(new Prompt(prompt, options)); // blocking response carries signature\n}","preventionTips":["Keep the anthropic-java SDK and spring-ai-anthropic versions aligned so signature_delta events are handled","Avoid proxies/interceptors that rewrite or filter streaming SSE events","If you rely on thinking replay in multi-turn, verify signatures exist before sending the next request","Prefer blocking calls when thinking replay is critical and streaming is not required"],"tags":["anthropic","streaming","thinking","signature","extended-thinking"],"backgroundTag":"unexpected-response-shape","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}