Claude Preserved thinking – Modifying a conversation now results in an error Anthropic's Claude Fable 5.1 API now rejects requests when prior conversation turns are modified, unless the `prefix_mismatch_behavior` is set to `"drop_block"`, a change enforced by default for new accounts created on or after August 31, 2026, 00:00 UTC. The check ensures reasoning produced under one set of instructions cannot be replayed under another, and Anthropic recommends using official products or SDKs, or treating the `messages` array as append-only to avoid errors. Messages /docs/en/intro Thinking Preserved thinking 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 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 /docs/en/build-with-claude/mid-conversation-system-messages for new instructions, turn-scoped system messages per-turn-reminders for per-turn reminders, mid-conversation tool changes /docs/en/build-with-claude/mid-conversation-system-messages mid-conversation-tool-changes for adding and removing tools, and per-message effort /docs/en/build-with-claude/effort change-effort-mid-conversation-beta 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 /docs/en/build-with-claude/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 /docs/en/managed-agents/overview , or the Claude Agent SDK https://code.claude.com/docs/en/agent-sdk/overview . 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 the messages 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. See Preserved thinking /docs/en/build-with-claude/thinking preserved-for-model for the exact per-model list. Nothing before the block has changed. The top-level 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 /docs/en/build-with-claude/compaction . The chain of earlier thinking blocks is unbroken. Earlier 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 /docs/en/api/beta-headers : "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. Who is affected Claude Fable 5.1. See Preserved thinking /docs/en/build-with-claude/thinking 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 /docs/en/api/beta-headers 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 at reason: "prefix binding mismatch" : path changed between this request and the previous one. Diff system , tools , and messages 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-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 /docs/en/build-with-claude/thinking-troubleshooting error-thinking-block-signature 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 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 | user , assistant , or system message system string or blocks tools 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 /docs/en/build-with-claude/mid-conversation-system-messages 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 /docs/en/build-with-claude/mid-conversation-system-messages 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 /docs/en/build-with-claude/mid-conversation-system-messages 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 /docs/en/build-with-claude/mid-conversation-system-messages 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 /docs/en/build-with-claude/mid-conversation-system-messages 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. 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: Compaction /docs/en/build-with-claude/compaction summarizes 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 parameter Context editing /docs/en/build-with-claude/context-editing clears 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": "