{"record":{"id":"e27e30e3ef989b8a","repo":"spring-projects/spring-ai","slug":"unexpected-reasoningcontent-block-neither-reasoni","errorCode":null,"errorMessage":"Unexpected reasoningContent block: neither reasoningText nor redactedContent is set","messagePattern":"Unexpected reasoningContent block: neither reasoningText nor redactedContent 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":80,"sourceCode":"\n\t/**\n\t * Capture a Bedrock {@link ReasoningContentBlock} as a Spring AI value object,\n\t * preserving its union member (reasoning text and signature, or redacted content)\n\t * without modification.\n\t * @param reasoningContentBlock the AWS SDK reasoning content block\n\t * @return a value object that can replay the same reasoning block\n\t */\n\tpublic static BedrockReasoningContent from(ReasoningContentBlock reasoningContentBlock) {\n\t\tAssert.notNull(reasoningContentBlock, \"reasoningContentBlock must not be null\");\n\t\tSdkBytes redacted = reasoningContentBlock.redactedContent();\n\t\tif (redacted != null) {\n\t\t\treturn new BedrockReasoningContent(null, null, redacted.asByteArray());\n\t\t}\n\t\tReasoningTextBlock reasoningText = reasoningContentBlock.reasoningText();\n\t\tif (reasoningText != null) {\n\t\t\treturn new BedrockReasoningContent(reasoningText.text(), reasoningText.signature(), null);\n\t\t}\n\t\tthrow new IllegalStateException(\n\t\t\t\t\"Unexpected reasoningContent block: neither reasoningText nor redactedContent is set\");\n\t}\n\n\t/**\n\t * Convert this value object back into an AWS SDK {@link ContentBlock} carrying a\n\t * {@code reasoningContent} block. The reasoning text and signature are replayed\n\t * unmodified; signatures are never regenerated.\n\t * @return the reasoning content block ready to be added to a Bedrock request\n\t */\n\tpublic ContentBlock toContentBlock() {\n\t\tReasoningContentBlock.Builder builder = ReasoningContentBlock.builder();\n\t\tif (this.redactedContent != null) {\n\t\t\tbuilder.redactedContent(SdkBytes.fromByteArray(this.redactedContent));\n\t\t}\n\t\telse {\n\t\t\tbuilder.reasoningText(ReasoningTextBlock.builder().text(this.text).signature(this.signature).build());\n\t\t}\n\t\treturn ContentBlock.fromReasoningContent(builder.build());","sourceCodeStart":62,"sourceCodeEnd":98,"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#L62-L98","documentation":"BedrockReasoningContent.from converts an AWS SDK ReasoningContentBlock into the value object. If the block has neither redactedContent nor reasoningText set — an invariant the Bedrock API guarantees — this IllegalStateException is thrown, indicating an unexpected/malformed response shape from the service or SDK.","triggerScenarios":"Calling BedrockReasoningContent.from(reasoningContentBlock) on a block where both reasoningText() and redactedContent() are null — a deserialized but empty reasoningContent block from a Bedrock streaming/complete response.","commonSituations":"SDK version mismatches where an empty reasoning block is emitted by newer models; proxying non-standard responses; hand-constructed ReasoningContentBlock passed to from() without any payload.","solutions":["Check reasoningContentBlock.reasoningText() and .redactedContent() for null before calling from().","Upgrade the AWS SDK / Spring AI Bedrock dependency — empty reasoning blocks may be an SDK serialization bug.","Treat as an unexpected API response: log the raw block and skip it rather than failing the whole response.","Verify the model actually supports extended thinking and is emitting well-formed reasoning blocks."],"exampleFix":"// before\nBedrockReasoningContent rc = BedrockReasoningContent.from(block);\n// after\nif (block.reasoningText() == null && block.redactedContent() == null) {\n    return; // skip malformed empty reasoning block\n}\nBedrockReasoningContent rc = BedrockReasoningContent.from(block);","handlingStrategy":"try-catch","validationCode":"if (block.reasoningText() == null && block.redactedContent() == null) {\n    logger.warn(\"Skipping empty reasoningContent block\");\n    return null;\n}","typeGuard":"boolean hasReasoningPayload(ReasoningContentBlock b) {\n    return b != null && (b.reasoningText() != null || b.redactedContent() != null);\n}","tryCatchPattern":"try {\n    rc = BedrockReasoningContent.from(block);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"neither reasoningText nor redactedContent\")) {\n        rc = null; // tolerate empty block\n    } else throw e;\n}","preventionTips":["Guard both reasoningText() and redactedContent() for null before converting","Keep AWS SDK and Spring AI versions aligned","Log raw blocks when unexpected response shapes appear","Treat empty reasoning blocks as skippable in proxies/aggregators"],"tags":["reasoning","invariant-violation","unexpected-response","bedrock"],"backgroundTag":"unexpected-api-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"}