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:
messages array is treated as append-only. These common patterns edit the prefix and invalidate the thinking after the edit:
system prompt each request (current time, token budget, mode flags)tools mid-sessionFor new requests the API checks:
system prompt, the set of tools in tools, and every message before the block. With server-side compaction the checked prefix starts at the most recent compaction block.thinking and redacted_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 beta header:
"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-level input_transformations array (on the message_start event when streaming)."error": the API rejects the request with a 400 invalid_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.
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.
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"
}
]
}reason: "prefix_binding_mismatch": something before the block at path changed between this request and the previous one. Diff system, tools, and messages up to that turn to find it.reason: "model_binding_mismatch": 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.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-01` value in the `anthropic-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.
Between two consecutive requests:
| Change between requests | Later thinking blocks |
|---|---|
| Append messages at the end | Valid |
Add a tool with defer_loading: 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 turn-scoped system message left in place | Valid |
Edit, reorder, or delete any earlier user, assistant, or system message | Invalid |
| Add a text block to an earlier user turn, or remove one you added last time | Invalid |
Change the top-level system string or blocks | Invalid |
Add, remove, rename, or edit a tool in tools | Invalid |
Remove a thinking block from the middle of the history and keep later ones | Invalid for every later thinking block |
| An image or document URL that returns different bytes on the next request | Invalid |
| The same turn-scoped message deleted or reworded on a later request | Invalid |
Each pattern replaces one kind of history edit with an API feature that has the same effect on the model without changing earlier bytes.
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.
systemIf 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).
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.
tool_addition and tool_removal, not by editing toolsIf 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_loading: 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_loading: 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.
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:
instructions parameter takes your own summarization prompt ("preserve every ticker, position size, and stated assumption").clear_tool_uses_20250919) or old thinking blocks oldest-first (clear_thinking_20251015) by rule.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:
thinking and redacted_thinking from every assistant turn you carry across, keeping text and tool_use, or send prefix_mismatch_behavior: "drop_block" and let the API strip them."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).
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.
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.
"error" (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" explicitly if you want items to error."drop_block" if you'd rather drop the affected blocks than fail. Log 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.
| Feature | What it replaces | Status | Header |
|---|---|---|---|
Controls for blocks that aren't preserved (thinking.block_binding.prefix_mismatch_behavior, input_transformations) | Choose reject or drop on a prefix mismatch, and see what was dropped | Beta | thinking-binding-controls-2026-08-01 |
Mid-conversation system messages (role: "system" in messages) | Rebuilding the top-level system prompt | Stable | None |
Turn-scoped system messages (clear_at: "next_user_message") | Injecting a reminder and deleting it next request | Beta | mid-conversation-system-clear-at-2026-08-21 |
Mid-conversation tool changes (tool_addition, tool_removal) | Editing the tools array | Beta | mid-conversation-tool-changes-2026-07-01 |
Compaction (instructions for a custom summary prompt) | Client-side summarization of old turns | Beta | compact-2026-01-12 |
Context editing (clear_tool_uses_20250919, clear_thinking_20251015) | Client-side deletion of old tool results or thinking | Beta | context-management-2025-06-27 |
Files API (file_id sources) | URLs whose content changes between requests | Stable | None |
Per-message effort (output_config.effort on a role: "system" message) | Changing top-level effort between requests (protects the prompt cache, not thinking: effort isn't part of the prefix) | Beta | 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-01The same beta names apply on Amazon Bedrock and Google Cloud. See Beta headers for how to send them with each SDK.
system, tools, and the shared messages prefix.prefix_mismatch_behavior: "drop_block" logs no prefix_binding_mismatch entries.system and tools are fixed for the session. Changes go in role: "system" messages and tool_addition / tool_removal blocks.file_id or base64, not mutable URLs.prefix_mismatch_behavior is set and its 400s or dropped entries are monitored.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?