MessagesThinking
Modifying a conversation now results in an error or a dropped block; how to check whether your integration does that and how to migrate.
On Claude Fable 5.1, changing prior turns in the conversation (the system
prompt, the tools
, or any earlier message) affects the API response. By default, it makes the API reject the request with an error, unless you opt to have the affected thinking blocks dropped from what the model sees instead (prefix_mismatch_behavior: "drop_block"
). The check is enforced by default for new accounts created on or after August 31, 2026, 00:00 UTC. There are more details in * How it works* and
Who is affected.When you send a block back, the API uses its signature
to check that the prior conversation is unchanged and that the current model can read the block. The check exists so that reasoning produced under one set of instructions can't be replayed under another, potentially adversarial set of instructions.
The API provides first-class alternatives to modify a conversation as it progresses, covering most use cases for transcript edits: mid-conversation system messages for new instructions, turn-scoped system messages for per-turn reminders, mid-conversation tool changes for adding and removing tools, and per-message effort to adjust depth of thinking per turn. The rest of this page covers how to tell whether your integration is affected and how to migrate common harness patterns to these features. As an added benefit, keeping everything before each thinking block byte-for-byte unchanged also keeps the prefix stable for prompt caching.
Whether you need to do anything depends on what manages your conversation history:
You use an official Claude product or SDK: Claude Code, claude.ai,Claude Managed Agents, or theClaude Agent SDK. These keep the prefix intact for you.You call the Messages API directly, from your own agent loop or any other setting. You should check your code and ensure that themessages
array is treated as append-only. These common patterns edit the prefix and invalidate the thinking after the edit:- Trimming or dropping older turns
- Summarizing older turns on the client and keeping recent ones
- Injecting a reminder into an earlier turn and removing it on the next request
- Rebuilding the
system
prompt each request (current time, token budget, mode flags) - Adding or removing entries in
tools
mid-session
How it works #
For new requests the API checks:
The model is the same or newer. A block is readable by the model that produced it and by later models, not by earlier ones. A conversation that moves to a newer model keeps its reasoning. A conversation that moves to an older model fails the model check for those blocks, and the API drops them for that request. SeePreserved thinkingfor the exact per-model list.Nothing before the block has changed. The top-levelsystem
prompt, the set of tools intools
, and every message before the block. With server-side compaction the checked prefix starts at the most recentcompaction block.The chain of earlier thinking blocks is unbroken. Earlierthinking
andredacted_thinking
blocks aren't part of the prefix, but each thinking block records the one before it, across turns. You can remove thinking blocks from the front of the history. Removing one from the middle invalidates every thinking block after it.
A block that fails the model check is always dropped. For a prefix mismatch you choose what happens with thinking.block_binding.prefix_mismatch_behavior
, which requires the thinking-binding-controls-2026-08-01
"drop_block"
: the API removes the block and every thinking block after it in the conversation, and the request succeeds. Dropped blocks aren't billed. The response lists them in a top-levelinput_transformations
array (on themessage_start
event when streaming)."error"
: the API rejects the request with a 400invalid_request_error
that names the first failing block.
The default is "error"
. The header lets you set the field and adds input_transformations
to responses.
Who is affected #
Claude Fable 5.1. See Preserved thinking for the model list.
On Claude Fable 5.1, the API enforces the check for new accounts. A new account is one created on or after August 31, 2026, 00:00 UTC. The same definition applies on the Claude API and on cloud platforms. Later models will enforce the check for all users.
A request that sets prefix_mismatch_behavior
opts into enforcement regardless of account age, which is how you test from an older account. To check whether your account is enforced by default, send a request that edits history without the beta header: a 400 that names the header means enforced.
How to tell whether your integration is impacted #
Capture the exact request bodies your integration sends over a few normal turns, including a compaction or a tool change if your product does those. For each pair of consecutive requests, compare system
, tools
, and the shared part of messages
. They should be byte-identical up to the newly appended turns.
Then confirm against the API. With the thinking-binding-controls-2026-08-01
beta header and claude-fable-5-1
, set thinking.block_binding.prefix_mismatch_behavior
to "drop_block"
and run a normal multi-turn session through your integration. This request is the second turn of such a session, sending back the first response's assistant turn exactly as received:
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: thinking-binding-controls-2026-08-01" \
-d '{
"model": "claude-fable-5-1",
"max_tokens": 16000,
"thinking": {
"type": "adaptive",
"block_binding": { "prefix_mismatch_behavior": "drop_block" }
},
"system": "You are a coding agent.",
"messages": [
{ "role": "user", "content": "Fix the failing test." },
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "EqQBCkYIBxgCKkD..." },
{ "type": "text", "text": "I need to see the test first. Which file is it in?" }
]
},
{ "role": "user", "content": "tests/test_auth.py" }
]
}'
Every response then carries a top-level input_transformations
array. Log it on each turn:
{
"input_transformations": [
{
"type": "thinking_dropped",
"path": "messages.1.content.0",
"reason": "prefix_binding_mismatch"
}
]
}
Empty on every turn: your integration keeps history intact.something before the block atreason: "prefix_binding_mismatch"
:path
changed between this request and the previous one. Diffsystem
,tools
, andmessages
up to that turn to find it.the conversation moved to a model that can't read the earlier model's blocks (a router, a fallback). Not a bug in your integration. Keep sending the blocks and let the API drop what the current model can't read.reason: "model_binding_mismatch"
:
This works from any account, because setting the field opts the request into enforcement. To fail loudly in CI instead, set "error"
. The 400 begins:
messages.1.content.0: 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".
Without the beta header on the request, the message continues: That setting requires the thinking-binding-controls-2026-08-01value in theanthropic-beta header.
The message usually ends with a sentence naming what changed, for example that the system
prompt or the tools
list differs from when the block was created.
See Troubleshooting thinking for every variant of this error.
What counts as an edit #
Between two consecutive requests:
| Change between requests | Later thinking blocks |
|---|---|
| Append messages at the end | Valid |
Add a tool with defer_: true that nothing has referenced yet |
Valid |
Remove thinking blocks from the start of the history (every thinking block before some point) |
Valid |
Change any request parameter outside system , tools , and messages (max_tokens , output_config , tool_choice , metadata , and so on) |
Valid |
Add, move, or remove cache_control markers |
Valid |
| A rotating signed URL that returns the same bytes | Valid |
| Server-side compaction or context editing removes or replaces content | Valid (the check compares what you sent, not the server's edited copy) |
| A cleared | |
user
, assistant
, or system
messagesystem
string or blockstools
thinking
block from the middle of the history and keep later ones## Update your integration
Each pattern replaces one kind of history edit with an API feature that has the same effect on the model without changing earlier bytes.
Append assistant turns exactly as returned
Store the content
array from each response and send it back unchanged as the assistant turn, every block type in the order received, including thinking
blocks whose thinking
field is empty. Don't reserialize through an intermediate type that drops unknown block types or empty fields.
Add instructions with a mid-conversation system message, not by editing system
If your code rebuilds the top-level system
prompt each request (current time, token budget, mode flag, newly discovered project context), every thinking block in the conversation fails the check. Freeze system
at session start, and when something changes append a role: "system" message at the point in
messages
where it becomes true:
{
"role": "system",
"content": "The user switched the workspace to read-only mode. Do not write files until told otherwise."
}
The model treats it with system-prompt authority, and everything before it is unchanged. No beta header is needed on Claude Fable 5.1. In a tool loop, place it after the tool_result
user message, never between an assistant tool_use
and its tool_result
(see Limitations).
Send per-turn reminders as turn-scoped system messages
The most common history edit is the per-turn nudge: a line appended after each batch of tool results ("request independent reads together", "you haven't updated the user in a while") and removed on the next request so reminders don't pile up. Removing it is the edit.
Instead, send the nudge as a mid-conversation system message with clear_at: "next_user_message"
after the tool_result
user message (beta header mid-conversation-system-clear-at-2026-08-21
). This messages
array is the request after two tool rounds. messages[3]
is the previous request's nudge, left in place, and messages[6]
is this request's copy:
[
{ "role": "user", "content": "Fix the failing test." },
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "..." },
{
"type": "tool_use",
"id": "toolu_01",
"name": "read_file",
"input": { "path": "tests/test_auth.py" }
}
]
},
{
"role": "user",
"content": [{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "..." }]
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "Request every independent read in one turn."
},
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "..." },
{
"type": "tool_use",
"id": "toolu_02",
"name": "read_file",
"input": { "path": "src/auth.py" }
}
]
},
{
"role": "user",
"content": [{ "type": "tool_result", "tool_use_id": "toolu_02", "content": "..." }]
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "Request every independent read in one turn."
}
]
A tool_result
-only user message counts as the "next user message", so messages[3]
is already cleared: it renders nothing and costs no input tokens, but it's still in the array, so the thinking in messages[4]
stays valid. messages[6]
is what the model sees this turn. On later requests keep both where they are and append the next copy after the next tool_result
message. Turn-scoped messages carry text
only and take no cache_control
. Put the cache breakpoint on the preceding user turn. See Turn-scoped system messages.
Without the beta, append the nudge as a text
block after the tool_result
blocks in the same user message, and leave earlier copies in place. The model acts on the newest one.
Change tools with tool_addition
and tool_removal
, not by editing tools
If the set of tools changes mid-session (a tool unlocks after authentication, a dangerous tool is withdrawn after a mode switch), don't edit tools
. Declare the full set at session start and use mid-conversation tool changes to offer or withdraw a tool from that point on (beta header mid-conversation-tool-changes-2026-07-01
). A tool that isn't available yet gets defer_: true
and a later tool_addition
block, same shape as this tool_removal
:
{
"role": "system",
"content": [
{ "type": "tool_removal", "tool": { "type": "tool_reference", "name": "delete_branch" } },
{ "type": "text", "text": "Branch deletion is disabled for the rest of this session." }
]
}
A tool whose schema you learn mid-session (an MCP server discovered at runtime) can be appended to tools
with defer_: true
and offered with tool_addition
. An unreferenced deferred tool isn't part of the prefix, so appending it is safe. Appending a regular tool isn't.
Trim context on the server where you can
Client-side truncation and summarization are the second most common edit: drop or summarize the oldest turns and keep the recent ones verbatim. The recent turns' thinking blocks were produced while the history you removed was still in place, so they fail the check. The server-side equivalents don't count as edits, because the check compares the conversation as you sent it:
Compactionsummarizes older turns into a compaction block when the context approaches a threshold you set, and the checked prefix restarts from that block. Itstakes your own summarization prompt ("preserve every ticker, position size, and stated assumption").instructions
parameterContext editingclears old tool results (clear_tool_uses_20250919
) or old thinking blocks oldest-first (clear_thinking_20251015
) by rule.
Custom compaction on the client
This check doesn't prohibit client-side compaction. The rule is narrower: don't keep a thinking block behind a prefix you've rewritten.
Simple compaction is the recommended shape and needs no changes. When the conversation grows too long, summarize it into one message and start the next request with that summary plus the new user turn, replaying no earlier turns or thinking blocks: messages
becomes [{"role": "user", "content": "<summary of the session so far>\n\n<the next instruction>"}]
. No earlier thinking remains, so nothing fails, and the model thinks afresh on the compacted conversation. Claude models are trained on long-horizon tasks with this scheme, and it performs comparably to more elaborate ones for most workloads. It resets the prompt cache at the compaction point, as any compaction does.
Two other common shapes fail as written and need one change each:
Keep-tail compaction summarizes older turns and keeps the most recent turns verbatim. The kept turns' thinking blocks were produced against the full history, so they fail behind the summary. Fix: stripthinking
andredacted_thinking
from every assistant turn you carry across, keepingtext
andtool_use
, or sendprefix_mismatch_behavior: "drop_block"
and let the API strip them.Background compaction builds the summary off the critical path and swaps it in while the conversation continues, so every turn produced in the meantime has thinking that predates the swap. Fix: send"drop_block"
on every request that still carries thinking blocks produced before the swap (or strip those blocks yourself;input_transformations
on the first response after the swap lists exactly which ones), or compact synchronously.
Snipping individual turns out of the middle of the transcript invalidates everything after them, and no client-side shape avoids that. Use a mid-conversation system message for the instruction change you were making, or server-side context editing for selective removal.
Don't compact in the middle of a tool round: an assistant turn whose tool_use
is still waiting on a tool_result
should go back with its thinking intact, so the model finishes the round with its reasoning (see Preserving thinking blocks).
Reference files by ID, not by URL that changes content
For an image
or document
block with a url
source, the fetched bytes are part of the checked prefix and the URL string isn't. A "latest screenshot" endpoint or an edited document invalidates later thinking. A rotating signed URL for the same file doesn't. For content you reference across turns, upload it once with the Files API and use the file_id
, or send base64.
Decide what happens on a mismatch
Once your integration is append-only, choose a prefix_mismatch_behavior
for production. It governs only prefix mismatches. A block the current model can't read (after a router switch or server-side fallback) is always dropped, and reported in input_transformations
when the beta header is sent.
(the default) if a prefix mismatch can only mean a bug in your code. You find out from a 400 in testing rather than from silently dropped blocks. In the Message Batches API, the unset default drops failing blocks instead of failing the batch item; set"error"
"error"
explicitly if you want items to error.if you'd rather drop the affected blocks than fail. Log"drop_block"
input_transformations
.
If you catch the 400 in production, retrying the same request won't clear it. Retry with prefix_mismatch_behavior: "drop_block"
(and the beta header), which removes exactly the blocks that fail, including any in an assistant turn whose tool_use
is still waiting on its tool_result
. The drop applies to that request only, so keep sending "drop_block"
(and the beta header) for the rest of the session. Without the beta, strip every thinking
and redacted_thinking
block from the history, leaving each turn's text
and tool_use
blocks in place, and retry once. Then fix the edit that caused it.
API features used on this page #
| Feature | What it replaces | Status | Header |
|---|---|---|---|
thinking.block_binding.prefix_mismatch_behavior , input_transformations ) |
thinking-binding-controls-2026-08-01
Mid-conversation system messages(role: "system"
in messages
)system
promptTurn-scoped system messages(clear_at: "next_user_message"
)mid-conversation-system-clear-at-2026-08-21
Mid-conversation tool changes(tool_addition
, tool_removal
)tools
arraymid-conversation-tool-changes-2026-07-01
Compaction(instructions
for a custom summary prompt)compact-2026-01-12
Context editing(clear_tool_uses_20250919
, clear_thinking_20251015
)context-management-2025-06-27
Files API(file_id
sources)Per-message effort(output_config.effort
on a role: "system"
message)mid-conversation-output-config-2026-07-01
To combine headers in one request:
anthropic-beta: thinking-binding-controls-2026-08-01,mid-conversation-system-clear-at-2026-08-21,mid-conversation-tool-changes-2026-07-01
The same beta names apply on Amazon Bedrock and Google Cloud. See Beta headers for how to send them with each SDK.
Checklist #
- If an official Claude product or SDK (Claude Code, claude.ai, Claude Managed Agents, the Claude Agent SDK) manages your conversation history, stop here.
- Consecutive request bodies are byte-identical in
system
,tools
, and the sharedmessages
prefix. - A full session under
prefix_mismatch_behavior: "drop_block"
logs noprefix_binding_mismatch
entries. - Assistant turns go back byte-for-byte as returned, all block types included.
- Top-level
system
andtools
are fixed for the session. Changes go inrole: "system"
messages andtool_addition
/tool_removal
blocks. - Per-turn reminders are turn-scoped system messages (or trailing text blocks) that are appended fresh and never removed.
- Context is trimmed by compaction or context editing, or by a client-side compaction that leaves no thinking blocks behind the rewritten prefix and never splits a tool round.
- Cross-turn files are
file_id
or base64, not mutable URLs. - A production
prefix_mismatch_behavior
is set and its 400s or dropped entries are monitored.
Next steps #
Diagnose and fix the most common thinking failures: configuration 400 errors, empty or missing thinking blocks, max_tokens stops, and cache misses.
Change system instructions or tool availability partway through a conversation without invalidating the cached prefix that came before them.
Server-side context compaction for managing long conversations that approach context window limits.
Cache prompt prefixes with cache_control
to cut costs and latency, using automatic caching or explicit breakpoints with 5-minute or 1-hour TTLs.
Was this page helpful?