spring-projects/spring-ai · error · java.lang.IllegalStateException
Unexpected reasoningContent block: neither reasoningText…
Error message
Unexpected reasoningContent block: neither reasoningText nor redactedContent is set
What it means
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.
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.
Example fix
// before
BedrockReasoningContent rc = BedrockReasoningContent.from(block);
// after
if (block.reasoningText() == null && block.redactedContent() == null) {
return; // skip malformed empty reasoning block
}
BedrockReasoningContent rc = BedrockReasoningContent.from(block); Defensive patterns
Strategy: try-catch
Validate before calling
if (block.reasoningText() == null && block.redactedContent() == null) {
logger.warn("Skipping empty reasoningContent block");
return null;
} Type guard
boolean hasReasoningPayload(ReasoningContentBlock b) {
return b != null && (b.reasoningText() != null || b.redactedContent() != null);
} Try / catch
try {
rc = BedrockReasoningContent.from(block);
} catch (IllegalStateException e) {
if (e.getMessage().contains("neither reasoningText nor redactedContent")) {
rc = null; // tolerate empty block
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- signature must be set when text is set
- Chat completion is not supported for this model:
- Connection to blocked internal address rejected for host ''
- Documents are ingested via data source sync, not direct add.
- Documents are managed via data source, not direct delete.
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/e27e30e3ef989b8a.
Report an issue: GitHub.
Appendix: source
Thrown at models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockReasoningContent.java:80
/**
* Capture a Bedrock {@link ReasoningContentBlock} as a Spring AI value object,
* preserving its union member (reasoning text and signature, or redacted content)
* without modification.
* @param reasoningContentBlock the AWS SDK reasoning content block
* @return a value object that can replay the same reasoning block
*/
public static BedrockReasoningContent from(ReasoningContentBlock reasoningContentBlock) {
Assert.notNull(reasoningContentBlock, "reasoningContentBlock must not be null");
SdkBytes redacted = reasoningContentBlock.redactedContent();
if (redacted != null) {
return new BedrockReasoningContent(null, null, redacted.asByteArray());
}
ReasoningTextBlock reasoningText = reasoningContentBlock.reasoningText();
if (reasoningText != null) {
return new BedrockReasoningContent(reasoningText.text(), reasoningText.signature(), null);
}
throw new IllegalStateException(
"Unexpected reasoningContent block: neither reasoningText nor redactedContent is set");
}
/**
* Convert this value object back into an AWS SDK {@link ContentBlock} carrying a
* {@code reasoningContent} block. The reasoning text and signature are replayed
* unmodified; signatures are never regenerated.
* @return the reasoning content block ready to be added to a Bedrock request
*/
public ContentBlock toContentBlock() {
ReasoningContentBlock.Builder builder = ReasoningContentBlock.builder();
if (this.redactedContent != null) {
builder.redactedContent(SdkBytes.fromByteArray(this.redactedContent));
}
else {
builder.reasoningText(ReasoningTextBlock.builder().text(this.text).signature(this.signature).build());
}
return ContentBlock.fromReasoningContent(builder.build());View on GitHub (pinned to 98a7beda4f)