{"record":{"id":"6dbc5bfd577f80fa","repo":"spring-projects/spring-ai","slug":"signature-must-be-set-when-text-is-set","errorCode":null,"errorMessage":"signature must be set when text is set","messagePattern":"signature must be set when text is set","errorType":"exception","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockReasoningContent.java","lineNumber":169,"sourceCode":"\n\t\tpublic Builder text(@Nullable String text) {\n\t\t\tthis.text = text;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder signature(@Nullable String signature) {\n\t\t\tthis.signature = signature;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder redactedContent(byte @Nullable [] redactedContent) {\n\t\t\tthis.redactedContent = redactedContent != null ? redactedContent.clone() : null;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic BedrockReasoningContent build() {\n\t\t\tif (this.text != null && this.signature == null) {\n\t\t\t\tthrow new IllegalStateException(\"signature must be set when text is set\");\n\t\t\t}\n\t\t\treturn new BedrockReasoningContent(this.text, this.signature, this.redactedContent);\n\t\t}\n\n\t}\n\n}\n","sourceCodeStart":151,"sourceCodeEnd":177,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockReasoningContent.java#L151-L177","documentation":"BedrockReasoningContent.Builder.build enforces the Bedrock invariant that reasoning text must always be accompanied by a signature (used to verify the reasoning when replayed). Calling build() with text set but signature null throws this IllegalStateException.","triggerScenarios":"Programmatically building a BedrockReasoningContent (typically to replay reasoning in multi-turn tool-use conversations) via Builder.text(...) without calling .signature(...).","commonSituations":"Replaying assistant reasoning during multi-turn conversations: developers copy the text but drop the signature field; manually reconstructing reasoning content from logs that omitted the signature.","solutions":["Always pass the signature from the original ReasoningTextBlock alongside the text.","Persist both reasoningText.text() and reasoningText.signature() from prior turns and replay both.","If you have no signature, omit the text too — build a redactedContent-only reasoning block or drop the block.","Capture the signature at first receipt (BedrockReasoningContent.getSignature()) before serialization."],"exampleFix":"// before\nBedrockReasoningContent.builder()\n    .text(previous.getReasoningText())\n    .build();\n// after\nBedrockReasoningContent.builder()\n    .text(previous.getReasoningText())\n    .signature(previous.getReasoningSignature())\n    .build();","handlingStrategy":"validation","validationCode":"if (text != null && signature == null) {\n    throw new IllegalArgumentException(\"Cannot replay reasoning without signature\");\n}","typeGuard":"boolean isReplayable(BedrockReasoningContent rc) {\n    return rc.getText() == null || rc.getSignature() != null;\n}","tryCatchPattern":"try {\n    content = builder.build();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"signature must be set\")) {\n        // drop text or supply signature before rebuilding\n    } else throw e;\n}","preventionTips":["Persist signature together with reasoning text from every reasoning turn","Never reconstruct reasoning content from text-only logs","Use BedrockReasoningContent getters to copy both fields when replaying","Add an assert in serialization helpers that signature accompanies text"],"tags":["reasoning","builder-validation","missing-field","bedrock"],"backgroundTag":"missing-required-argument","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"}