Skip to content

Claude API 400 "thinking or redacted_thinking blocks in the latest assistant message cannot be modified": Causes and Fixes ​

A request to POST /v1/messages fails with HTTP 400 invalid_request_error when thinking or redacted_thinking blocks in your conversation history do not match what the API returned, or when the prefix before a thinking block no longer matches the conversation that produced it.

Short answer

With tool use, pass back every thinking and redacted_thinking block from the assistant turn exactly as received, including blocks whose thinking field is empty. If your error instead says Invalid signature in thinking block. The block is bound to a different conversation, your system, tools or earlier messages changed after the block was generated: either send the thinking-binding-controls-2026-08-01 beta header with thinking.block_binding.prefix_mismatch_behavior: "drop_block", or strip the offending block and every thinking and redacted_thinking block after it.

Last verified: 2026-11-14 against Anthropic API Errors — Common validation errors.

Symptoms and What the Error Means ​

If thinking blocks in the request do not match what the API returned, the request fails with HTTP 400 invalid_request_error. The message starts with the position of the offending block (for example messages.1.content.0) and contains:

text
`thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.

A different failure with a different message is the prefix check. It means a thinking block you sent back is bound to a different conversation. Its message starts with the position of the first failing block and reads:

text
messages.{i}.content.{j}: Invalid `signature` in `thinking` block. The block is bound to a different conversation. Remove the block, or set `thinking.block_binding.prefix_mismatch_behavior` to "drop_block".

If you configure thinking.block_binding without sending the thinking-binding-controls-2026-08-01 beta header, the API returns a 400 whose message ends in:

text
block_binding: Extra inputs are not permitted

Related errors on neighboring pages:

Why It Happens ​

  1. A framework or proxy that strips, reorders, truncates or re-serializes content blocks. Modifying any field, dropping redacted_thinking, or omitting a thinking block whose thinking string is "" breaks the latest assistant turn check.
  2. Storing only the text or tool_use blocks of assistant turns. When you reconstruct the assistant message from a database without the original thinking blocks (or with altered blocks), the next turn fails.
  3. Changing the system prompt or tool list mid-conversation. The prefix check compares system, tools and the messages before the thinking block with what was sent when the model produced it.
  4. Trimming or summarizing history. Editing or deleting earlier messages after the model has produced thinking blocks changes the conversation prefix.
  5. Switching between API keys / accounts or models in one conversation. A thinking block produced in one context can fail the binding check when replayed in another.

Fix 1: Pass Back Every thinking and redacted_thinking Block Unmodified ​

With tool use, pass back every thinking and redacted_thinking block from the assistant turn exactly as received, including blocks whose thinking field is empty. If your application filters content blocks by type before resending, include both thinking and redacted_thinking.

Failing request (POST /v1/messages) ​

Here the client modified the thinking text on the latest assistant message (or dropped an empty thinking block / redacted_thinking block) before sending back the tool_result:

json
{
  "model": "claude-opus-5-5",
  "max_tokens": 4096,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get weather for a city",
      "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  ],
  "messages": [
    {"role": "user", "content": "What is the weather in London?"},
    {
      "role": "assistant",
      "content": [
        {
          "type": "thinking",
          "thinking": "edited thinking text",
          "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve/op91XWHOEBLLqIOMfFG/UvLEczmEsUjavL..."
        },
        {
          "type": "tool_use",
          "id": "toolu_01A09q90qw90lq917835lq9",
          "name": "get_weather",
          "input": {"city": "London"}
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
          "content": "15C, cloudy"
        }
      ]
    }
  ]
}

Fixed request (POST /v1/messages) ​

Preserve the assistant content array in the exact order and byte-for-byte structure returned by the API — including thinking blocks (even when "thinking": "") and redacted_thinking blocks:

json
{
  "model": "claude-opus-5-5",
  "max_tokens": 4096,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get weather for a city",
      "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  ],
  "messages": [
    {"role": "user", "content": "What is the weather in London?"},
    {
      "role": "assistant",
      "content": [
        {
          "type": "thinking",
          "thinking": "",
          "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve/op91XWHOEBLLqIOMfFG/UvLEczmEsUjavL..."
        },
        {
          "type": "redacted_thinking",
          "data": "EmwKAhgBEgy3va3pzix/LafPsn4a..."
        },
        {
          "type": "tool_use",
          "id": "toolu_01A09q90qw90lq917835lq9",
          "name": "get_weather",
          "input": {"city": "London"}
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
          "content": "15C, cloudy"
        }
      ]
    }
  ]
}

Fix 2: Resolve Prefix Mismatches (The block is bound to a different conversation) ​

When a request fails the prefix check, resending the same history fails the same way. Compare system, tools and the messages before the block with what was sent when the model produced it.

To stop new conversations from failing, keep the history append-only: do not edit system, tools or earlier messages after the model has produced thinking blocks.

To continue a conversation that has already hit this error, do one of the following:

Option A: Set prefix_mismatch_behavior to "drop_block" ​

Send the thinking-binding-controls-2026-08-01 beta header (anthropic-beta: thinking-binding-controls-2026-08-01) and set thinking.block_binding.prefix_mismatch_behavior to "drop_block" on the retry and on every later request in that conversation.

On Claude Sonnet 5.5 and Claude Haiku 5.5, block_binding works only with thinking: {"type": "adaptive"} (see also Claude Opus 5.5):

json
{
  "model": "claude-sonnet-5-5",
  "max_tokens": 4096,
  "thinking": {
    "type": "adaptive",
    "block_binding": {
      "prefix_mismatch_behavior": "drop_block"
    }
  },
  "system": "Updated system prompt after turn 1.",
  "messages": [
    {"role": "user", "content": "Hello"},
    {
      "role": "assistant",
      "content": [
        {
          "type": "thinking",
          "thinking": "Initial thought...",
          "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfb..."
        },
        {"type": "text", "text": "Hi! How can I help?"}
      ]
    },
    {"role": "user", "content": "Continue with the new instructions."}
  ]
}

Remember to include the thinking-binding-controls-2026-08-01 beta header; without it, the API rejects block_binding with block_binding: Extra inputs are not permitted.

Option B: Strip the Failing Block and Every Thinking Block After It ​

If you do not use the beta header, strip the block the error names (for example messages.{i}.content.{j}) and every thinking and redacted_thinking block after it, both in that message and in every later message. Then retry and leave those blocks out of all later requests in that conversation:

json
{
  "model": "claude-sonnet-5-5",
  "max_tokens": 4096,
  "thinking": {
    "type": "adaptive"
  },
  "system": "Updated system prompt after turn 1.",
  "messages": [
    {"role": "user", "content": "Hello"},
    {
      "role": "assistant",
      "content": [
        {"type": "text", "text": "Hi! How can I help?"}
      ]
    },
    {"role": "user", "content": "Continue with the new instructions."}
  ]
}

Through a gateway ​

Both 400 errors come from the upstream API's validation of thinking and redacted_thinking blocks, and a gateway passes them through unchanged. When calling https://byesu.com (/v1/messages or /v1), keep assistant content arrays intact across tool turns, keep conversation prefixes append-only, and forward the anthropic-beta: thinking-binding-controls-2026-08-01 header whenever you use thinking.block_binding. For client routing issues, see Claude Code custom base URL errors or browse the error index.

FAQ ​

Why do I get "thinking or redacted_thinking blocks in the latest assistant message cannot be modified"? ​

The thinking blocks in your request do not match what the API returned. With tool use, you must pass back every thinking and redacted_thinking block from the assistant turn exactly as received, including blocks whose thinking field is empty.

Should I pass back thinking blocks whose thinking field is empty? ​

Yes. Pass back every thinking and redacted_thinking block from the assistant turn exactly as received, including blocks where the thinking field is empty. If your application filters content blocks by type before resending, include both thinking and redacted_thinking.

What does "The block is bound to a different conversation" mean? ​

It means the request failed the prefix check because a thinking block you sent back is bound to a different conversation. Compare system, tools and the messages before the block with what was sent when the model produced it.

Why do I get "block_binding: Extra inputs are not permitted"? ​

You sent thinking.block_binding without the thinking-binding-controls-2026-08-01 beta header. Include that beta header whenever you set thinking.block_binding.prefix_mismatch_behavior, and on Claude Sonnet 5.5 and Claude Haiku 5.5 combine it with thinking type "adaptive".

Got a problem? Contact support or join our group.