Bedrock Converse API: Tracking Down "No Response from the 10th Call" — Pitfalls When Using Extended Thinking × Tool Use
This page has been translated by machine translation. View original
TL;DR — Notes on Using Extended Thinking × Tool Use
A summary for those who aren't interested in the investigation process and just want the conclusion.
Problem: When using Extended Thinking (reasoningContent) together with tool use in the Bedrock Converse API, a ValidationException occurs when reasoningContent blocks become consecutive due to manipulation of the conversation history.
Cause: reasoningContent blocks are given a cryptographic signature (signature). This signature proves the authenticity of the block (that it was generated by Claude) and is not a hash of the text content. The API structurally validates not only the authenticity of the signature, but also whether the consecutive pattern of reasoning blocks matches the model's original output. When toolUse/toolResult blocks are excluded from the conversation history, reasoningContent blocks that were originally non-consecutive become adjacent, creating a consecutive pattern that did not exist in the original output, causing structural validation to fail.
Model output: [reasoning_A, toolUse, reasoning_B, text]
↓ toolUse excluded
After filter: [reasoning_A, reasoning_B, text]
^^^^^^^^^^^^^^^^^^^^^^^^
Consecutive pattern not present in model output → ValidationException
Countermeasures:
- If
reasoningContentblocks become consecutive after filtering, combine the text into a single block (since the signature does not validate text content, retaining either signature is sufficient) - Exclude messages whose
contentbecomes empty after filtering from the conversation history (to prevent cascade failures)
Reference: Anthropic Official Documentation — Extended thinking
the entire sequence of consecutive thinking blocks must match the outputs generated by the model during the original request
Introduction
"After asking the chat assistant about 10 questions, it stopped responding from the 11th question onward. No errors are displayed."
When I received this report, my first hypothesis was "context window exceeded." I figured it had hit the token limit after 10 exchanges and was producing an error about input being too long.
As it turned out, that hypothesis was wrong.
The actual cause was a violation of the message structure constraints in the Bedrock Converse API, and reaching that conclusion required an investigation spanning multiple data sources: CloudWatch, DynamoDB, Bedrock Model Invocation Logging, and direct API calls. This article walks through that investigation process.
Organizing the Symptoms
The application where the problem occurred is a chat assistant using the Bedrock Converse API. It operates with the following configuration:
- Model: Claude Sonnet 4 (Extended Thinking enabled)
- Tool use: Tools such as database queries via Function Calling
- Conversation history: Stored in DynamoDB, with the full history sent to the API on every request
Reported symptoms:
- Responds normally up to about 10 questions
- From the 11th question onward, no error displayed, the next question can be sent immediately
- No application crashes or error screens
The "no error displayed" aspect was tricky.
Chapter 1: Discovering the Silent Error
Nothing in CloudWatch
First I checked CloudWatch Logs. I searched the application's log group for logs around the relevant time, but found no WARN (level 40) or higher logs at all.
fields @timestamp, @message
| filter level >= 40
| sort @timestamp desc
| limit 100
Result: 0 entries. All logs were INFO (level 30) only.

There Were Clues in DynamoDB
Next I checked the chat history table in DynamoDB. This application stores chat messages encoded with gzip compression + Base64. When decoded:
[
{ "type": "note", "key": "InternalServerError" },
{ "type": "note", "key": "InternalServerError" },
{ "type": "note", "key": "InternalServerError" }
]
The errors were being saved to DynamoDB, but were not being sent to the client.
Looking at the code, there was a problem with the error handling when an error occurred during streaming response processing. While error information was being saved to the DB, both the SSE transmission to the client and log output were missing.
This is a pitfall specific to streaming processing. With a normal request-response, you can return an error via HTTP status code, but errors that occur during SSE streaming happen "after you've already started returning a response," so dedicated processing to notify the error is required. This notification processing was missing, causing the catch block to swallow the error without reaching the outer error handler.

Learnings at This Point
- No visible error ≠ No error occurring: The existence of the error could only be confirmed by directly checking the data in the persistence layer
- Silent catch blocks are dangerous: If you catch an error, you must always both log it and notify the user
Chapter 2: The True Nature of the ValidationException
When I found InternalServerError in DynamoDB, I still suspected "context window exceeded." However, estimating the token count of stored messages revealed that only about 15% of the 200K token limit was being used. I needed to look for a different cause.
| Item | Size |
|---|---|
| System prompt | ~10,000 characters |
| Total text content | ~26,000 characters |
| Total reasoning text | ~7,000 characters |
| Total reasoning signatures | ~19,000 characters |
Totaling approximately 62,000 characters (≈ 20,000–30,000 tokens). The model in use was Claude Sonnet 4.6 (context window 200K tokens), revealing that only about 15% of the limit was being used.
The application logs contained no error details whatsoever, and only the code InternalServerError was stored in DynamoDB. To identify the actual error, I enabled Bedrock Model Invocation Logging.
Enabling Bedrock Model Invocation Logging
-
Create a CloudWatch Logs log group: Retention period of 1 day (for temporary debugging)

-
Enable in Bedrock settings: Amazon Bedrock → Settings → Model invocation logging

-
Select logging destination: Select CloudWatch Logs only

-
Create an IAM role: A service role for Bedrock to write logs

-
Check in CloudWatch: Select the log group created in CloudWatch and review the logs

The Error Type Identified
After reproducing the issue in the staging environment, checking the Invocation Log revealed:
{
"operation": "ConverseStream",
"modelId": "jp.anthropic.claude-sonnet-4-6",
"errorCode": "ValidationException"
}
It was ValidationException, not InternalServerError. InternalServerError was the code assigned by the application-side catch block; the actual error type returned by the Bedrock API was ValidationException, indicating a request structure constraint violation.
Unfortunately, Bedrock Model Invocation Logging does not record the request body or detailed error messages when an error occurs. However, the body of the immediately preceding successful request is fully recorded, so I proceeded with the investigation using this as a clue.
Chapter 3: The Root Cause — Consecutive Reasoning Blocks
The Conversation History Filter Processing
First, some background. When sending conversation history to the Bedrock API, this application was selecting only the content types that need to be sent to the API (text, image, attachment, reasoning) using an allowlist approach.
This allowlist was designed when the application had no tool use functionality. At the time, only text and attachment existed, and an allowlist was sufficient. When reasoning (Extended Thinking) and image were subsequently added, they were added to the list, but tool blocks introduced later were left without being added to the allowlist.

tool blocks are UI display metadata that holds the tool execution state within the application (tool name, parameters, results) and do not need to be sent to the Bedrock API, so there is normally no problem with them not being included in the allowlist.
However, this design had an unexpected side effect.
The Difference Between Successful Requests and Stored Data
By comparing successful requests (Invocation Log) with stored messages (DynamoDB), a decisive discovery was made.
Through the allowlist filter, in most cases this works without issue. However, when the model "reconsiders" between tool calls — for example, inserting reasoning before calling another tool after seeing a tool execution result — the tool block may be the only separator between reasoning blocks:

Verifying the Hypothesis via AWS CLI
The analysis up to this point led to the hypothesis that "consecutive reasoning blocks cause an error." However, the detailed error message could not be obtained from Bedrock Model Invocation Logging. To confirm the hypothesis, I sent test payloads directly to the Bedrock Converse API via AWS CLI.
As the base for testing, I used the payload from the last successful request obtained from Invocation Logging. Since this payload contains actual signed reasoning blocks, it can accurately verify the API's constraints.
aws bedrock-runtime converse \
--region ap-northeast-1 \
--model-id jp.anthropic.claude-sonnet-4-6 \
--cli-input-json file://test-payload.json
Four tests were conducted, yielding the following results:
| Test | Payload Content | Result |
|---|---|---|
| Baseline | Successful request as-is (tool blocks excluded, reasoning non-consecutive) | Success |
| Test 1 | Make reasoning blocks consecutive (delete text between them) | ValidationException |
| Test 2 | Make reasoning consecutive in a past assistant message (not the latest) | ValidationException |
| Test 3 | Set assistant message content to an empty array |
ValidationException |
Test 1 result:
An error occurred (ValidationException) when calling the Converse operation:
The model returned the following errors:
messages.1.content.1: `thinking` or `redacted_thinking` blocks in the
latest assistant message cannot be modified. These blocks must remain
as they were in the original response.
Comparing the baseline and Test 1 confirmed that the position (index) of blocks shifting itself is not a problem — an error occurs only when reasoning blocks are adjacent.
From Test 2's result, it became clear that the API validates not only the latest assistant message, but all assistant messages in the conversation history. This means that if even one message in the history has consecutive reasoning blocks, all subsequent requests will fail.
The True Nature of the Signature — Clarifying the API Validation Mechanism Through Experiments
From the error message These blocks must remain as they were in the original response, it is clear that signature-based validation is involved. However, what exactly the signature validates is not clear from documentation alone.
The official Anthropic documentation states:
the entire sequence of consecutive thinking blocks must match the outputs generated by the model during the original request; you cannot rearrange or modify the sequence of these blocks
To determine what this "sequence" refers to — whether it's the text content or the block structure — I conducted additional experiments to identify what the signature validates.
What Does the Signature Validate?
Using the earlier test payload (two consecutive reasoning blocks [reasoning_A, reasoning_B, text]), I conducted four additional tests related to signatures:
| Test | Operation | Result |
|---|---|---|
| Test 4 | Rewrite reasoning_A's text to completely different content, signature unchanged | Success |
| Test 5 | Swap the signatures of reasoning_A and reasoning_B (text unchanged) | Success |
| Test 6 | Combine the text of two reasoning blocks into one block, using either signature | Success |
| Test 7 | Use a completely forged signature string | ValidationException |
Test 7 error message:
messages.1.content.0: Invalid `signature` in `thinking` block
From these results, the role of the signature became clear:
1. The signature is not a hash of the text content
In Test 4, completely rewriting the text still succeeded, and in Test 5, swapping signatures also succeeded. The signature is not tied to the content of reasoningText.
2. The signature is a proof of authenticity that "Claude generated this"
Only Test 7 with a forged signature failed. The role of the signature is to prove that the block was generated by the Claude API (an authenticity proof). Conceptually it is similar to a JWT (JSON Web Token), signed with the server's private key and verified with the same key.

3. The API is stateless — the signature encapsulates the "state"
LLM APIs are inherently stateless. Without storing conversation history on the server side, how can it verify "whether it matches the original output"? The answer is that the signature itself encapsulates the information needed for verification. It is the same mechanism by which JWT can verify token authenticity without a server-side session store.

4. Consecutive pattern validation is a separate structural check from the signature
The signature validates "whether the block was generated by Claude" and the consecutive pattern validation validates "whether the structural pattern matches the model's original output." These are two separate layers of validation:
- Signature validation (confirmed in Test 7): Whether the block was generated by the Claude API
- Structural validation (confirmed in Test 1): Whether the consecutive pattern of reasoning blocks matches the model's output

What the Test Results Mean
This finding directly affects countermeasures. Since the signature does not validate text content, it is possible to combine the text of consecutive reasoning blocks into a single block (confirmed in Test 6). This is a superior countermeasure compared to simply deleting blocks, as it resolves the consecutive pattern without losing the model's thinking context.
Note that the same documentation permits entirely omitting thinking blocks from previous turns (except when using tools). What becomes a problem is "creating a consecutive pattern that did not exist in the original output."
Test 3 result:
An error occurred (ValidationException) when calling the Converse operation:
The content field in the Message object at messages.1 is empty.
Add a ContentBlock object to the content field and try again.
This also confirmed the cascade failure mechanism (details in Chapter 4).

Why It Occurs at a Specific Number of Exchanges
This problem does not occur with every exchange. The trigger is a pattern where only a reasoning block is inserted between tool calls.
For example, when a tool call fails and the model retries:
- reasoning (thinking about query) → text → tool (executed, failed)
- reasoning (thinking about correction) → tool (re-executed, failed)
- reasoning (thinking about further correction) → tool (succeeded) → text (explaining results)
In step 2, there is no text block between reasoning and tool, so excluding the tool results in consecutive reasoning blocks.
In early exchanges, text blocks often exist before and after tool blocks, so reasoning does not become consecutive after filtering. As the number of exchanges increases, tool retries and compound calls occur, raising the probability that this pattern will appear. The reproducibility of "around the 10th question" is the reason for this.

Chapter 4: Cascade Failure — Once It Breaks, It Stays Broken Permanently
In addition to the root cause, a cascade failure occurs where once a failure happens, all subsequent requests fail permanently. This made the problem even more serious.
This failure pattern is not limited to this case — it can occur in any chat application that persists conversation history and resends it each time. If an incomplete assistant message is saved when an error occurs, that broken message will continue to be included in all subsequent requests.

The mechanism in this case:
- On the initial failure, the assistant message is saved in an incomplete state (with no valid content blocks)
- On the next request, all blocks of this message are excluded by the filter, sending an assistant message with empty content to Bedrock
- Empty content also causes
ValidationException→ permanent failure loop
// After filtering, content becomes empty
{
"role": "assistant",
"content": [] // Violates Bedrock API constraints
}
As confirmed in Test 3 from the previous chapter, an empty content array also returns a ValidationException.
In other words, even if the root cause (consecutive reasoning blocks) is fixed, chats that failed in the past remain permanently broken. Unless the processing to skip empty-content messages is also addressed, existing broken chats cannot be recovered.

Fixes and Countermeasures
Fix ①: Resolving Silent Errors
Log output and client notification were added to the error handling during streaming processing. Errors during SSE streaming need to be notified through a different path than normal HTTP error responses, making this an easy point to overlook.
Fix ②: Resolving the Root Cause
When sending conversation history to the Bedrock API, the following two points need to be addressed in the filter processing.
1. Resolving consecutive reasoning blocks
When reasoning blocks become consecutive after excluding tool blocks, combine the consecutive reasoning blocks into one. As noted above, since the signature does not validate text content, combining the text and retaining either signature will pass validation. This allows the consecutive pattern to be resolved while preserving the model's thinking context.
// Example of resolving consecutive reasoning after excluding tool blocks
function sanitizeContentBlocks(blocks: ContentBlock[]): ContentBlock[] {
const filtered = blocks.filter(b => b.type !== 'toolUse' && b.type !== 'toolResult');
// Combine consecutive reasoning blocks' text into a single block
const result: ContentBlock[] = [];
for (const block of filtered) {
const prev = result[result.length - 1];
if (prev?.type === 'reasoning' && block.type === 'reasoning') {
prev.reasoningText += '\n\n' + block.reasoningText;
// Since the signature is not tied to text content, keep the first block's signature as-is
} else {
result.push({ ...block });
}
}
return result;
}
2. Skipping empty-content messages
Messages whose content becomes empty after filtering are excluded from the conversation history. This prevents cascade failures from chats that failed in the past.
// Example of skipping messages with empty content
const messages = history
.map(msg => ({ ...msg, content: sanitizeContentBlocks(msg.content) }))
.filter(msg => msg.content.length > 0);
Reflection on the Investigation Process
Here is a summary of the methods used in this investigation and the effectiveness of each.
| Method | What It Revealed | Limitations |
|---|---|---|
| CloudWatch Logs | The fact that no logs were being output was itself a clue | No direct information since errors were caught and not logged |
| DynamoDB | Error codes, overall message structure | Detailed error messages were not saved |
| Bedrock Model Invocation Logging | Actual error type (ValidationException), successful request payload |
Request body and error messages at failure time are not recorded |
| Cross-referencing DynamoDB × code × Invocation Log | Identification of root cause | — |
| Direct API calls via AWS CLI (7 patterns) | Exact wording of error messages, confirmation of hypothesis, that all messages are validated, identification of what the signature validates | — |

The most effective approach was "cross-referencing multiple data sources." A single log source did not reveal the full picture; the cause could only be identified by combining the stored data in DynamoDB × the successful payload from the Invocation Log × static analysis of the code.
Summary
For Bedrock Converse API Users
- When combining Extended Thinking with tool use, be careful to ensure that reasoningContent blocks do not become consecutive when reconstructing conversation history
- When filtering specific content types from conversation history, verify that the block order after filtering satisfies the API's constraints
- The
signatureof a reasoning block is not a hash of the text content, but a proof of authenticity that Claude generated it. Combining or rewriting text is permitted, but forged signatures or consecutive patterns that did not exist in the original output will be rejected
As a Debugging Methodology
- Suspect silent errors: Even when no error is visible to the user, error information may remain in the persistence layer
- Reject hypotheses quickly: Rather than being fixated on the assumption of "context window exceeded," the token count should have been measured and rejected early
- Make use of Bedrock Model Invocation Logging: The most direct means of verifying the reality of API calls. Since it can be temporarily enabled and immediately disabled, it should be actively used during debugging
- Cross-reference multiple data sources: When a single log source is insufficient, analyze across stored data, application logs, and service logs
