Sweep 22 Sep 2026 · 17:19Z Build v2.1.280 501 read Stable v2.1.267 Latest v2.1.280 Next v2.1.280 Feeds RSS JSON llms.txt Unofficial
One change · api

preserved-thinking changed

build-with-claude/preserved-thinking

Nearest release: v2.1.261, published under an hour before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Recorded here
Lines+977added
Lines−214removed
From line 1 where the diff opens
First seen 1 Sep 2026 this site's first read of the page
Recorded edits10to this page, all time

## Switching models mid-conversation ## Keeping the prefix unchanged ### What the API does with an invalid block ### Set the mismatch behavior and read `input_transformations` ### When the API enforces the check ### What counts as an edit ### Check whether your code edits the prefix ## Make changes without editing the prefix ### Send assistant turns back exactly as returned ### Add instructions with a mid-conversation system message ### Add or remove tools with `tool_addition` and `tool_removal` ### Change effort with a per-message `output_config` ### Trim context on the server ### Compact on the client #### Simple compaction (recommended) #### Keep-tail compaction #### Patterns that don't work with preserved thinking ### Reference files by ID, not by a URL whose content changes ## How it works ## Who is affected ## How to tell whether your integration is impacted ## What counts as an edit ## Update your integration ### Append assistant turns exactly as returned ### Add instructions with a mid-conversation system message, not by editing `system` ### Change tools with `tool_addition` and `tool_removal`, not by editing `tools` ### Trim context on the server where you can ### Custom compaction on the client ### Reference files by ID, not by URL that changes content ### Decide what happens on a mismatch ## API features used on this page ## Checklist

The whole hunk

from line 1, old and new numbered
/
lines

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 1
11---
22title: Preserved thinking
33url: https://platform.claude.com/docs/en/build-with-claude/preserved-thinking
4description: Modifying a conversation now results in an error or a dropped block; how to check whether your integration does that and how to migrate.
4description: Preserved thinking lets a model use a thinking block from an earlier turn only if that model or an earlier one produced it and nothing before the block has changed.
55---
66 
7On 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](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-it-works)* and *[Who is affected](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#who-is-affected).*
8 
9When 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.
10 
11The API provides first-class alternatives to modify a conversation as it progresses, covering most use cases for transcript edits: [mid-conversation system messages](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) for new instructions, [turn-scoped system messages](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) for per-turn reminders, [mid-conversation tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes) for adding and removing tools, and [per-message effort](https://platform.claude.com/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](https://platform.claude.com/docs/en/build-with-claude/prompt-caching).
12 
13Whether you need to do anything depends on what manages your conversation history:
14 
15* **You use an official Claude product or SDK:** Claude Code, claude.ai, [Claude Managed Agents](https://platform.claude.com/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.
16 
17* **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:
18 
19 * Trimming or dropping older turns
20 * Summarizing older turns on the client and keeping recent ones
21 * Injecting a reminder into an earlier turn and removing it on the next request
22 * Rebuilding the `system` prompt each request (current time, token budget, mode flags)
23 * Adding or removing entries in `tools` mid-session
24 
25## How it works
26 
27For new requests the API checks:
28 
29* **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](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-for-model) for the exact per-model list.
30* **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](https://platform.claude.com/docs/en/build-with-claude/compaction).
31* **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.
32 
33A 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](https://platform.claude.com/docs/en/api/beta-headers):
34 
35* `"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).
36* `"error"`: the API rejects the request with a 400 `invalid_request_error` that names the first failing block.
37 
38The default is `"error"`. The header lets you set the field and adds `input_transformations` to responses.
39 
40## Who is affected
41 
42Claude Fable 5.1. See [Preserved thinking](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-thinking) for the model list.
43 
44On 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.
45 
46A 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.
7Preserved thinking is a property of newer Claude models that guards against distillation. It decides whether the model can use a thinking block that you send back from an earlier turn. Starting with Claude Fable 5.1, when a `thinking` or `redacted_thinking` block comes back in a request, the API checks the block's `signature` for two things:
8 
9* **The model is the one that produced the block, or a newer one.** A model reads its own thinking blocks and those of earlier models. Claude Fable 5.1 reads blocks from Claude Opus 5, but Claude Opus 5 can't read blocks from Claude Fable 5.1. If the current model can't read a block, the API drops it from that request without an error. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
10* **Nothing before the thinking block has changed.** The top-level `system` prompt, `tools`, and `messages` before the block are its prefix. If the prefix differs from what you sent when the block was produced, that block and every later thinking block are invalid, and the API rejects the request with a 400 error or drops the invalid blocks, whichever you choose. See [Keeping the prefix unchanged](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#prefix-check).
11 
12The model check applies to every account. The API enforces the prefix check by default for accounts created on or after August 31, 2026, 00:00 UTC. On older accounts, it enforces the prefix check only on requests that set `thinking.block_binding.prefix_mismatch_behavior`. **Later models will enforce the prefix check for all accounts**, so make your integration append-only now.
13 
14## Switching models mid-conversation
15 
16Claude Fable 5.1 and Claude Mythos 5.1 read thinking blocks produced by each other and by earlier Claude models. No earlier model reads thinking blocks from Claude Fable 5.1 or Claude Mythos 5.1.
17 
18* **A conversation that moves up to Claude Fable 5.1 keeps its reasoning.** The earlier model's thinking blocks stay readable, so the model thinks as usual from the first turn after the switch.
19* **A conversation that moves down to an earlier model loses Claude Fable 5.1's reasoning for that request.** This happens when a router sends a turn to a cheaper model, after a [classifier refusal fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback), or during a [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback). The API removes the unreadable blocks before the prompt reaches the model. They aren't billed and don't count toward `input_tokens`.
20 
21Keep sending the full history on every request, thinking blocks included, and let the API drop what the current model can't read. The API never edits your `messages` array, so the dropped blocks stay in your history. When the same history goes back to Claude Fable 5.1, its blocks are readable again, along with the earlier model's thinking. The reasoning is lost for good only if your client removes the blocks itself, for example a harness that strips thinking on a model switch or rebuilds the history from what each model used.
22 
23![Animation: switching to Claude Opus skips Claude Fable 5.1's thinking for that turn; switching back, everything is read again](https://platform.claude.com/docs/images/preserved-thinking-model-switch.gif)
24 
25With the `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers), the response lists each dropped block in a top-level `input_transformations` array with `reason: "model_binding_mismatch"`:
26 
27```json
28{
29 "input_transformations": [
30 {
31 "type": "thinking_dropped",
32 "path": "messages.3.content.0",
33 "reason": "model_binding_mismatch"
34 }
35 ]
36}
37```
38 
39Without the header, the drop is silent. This entry isn't a bug in your integration, and `prefix_mismatch_behavior` has no effect on it: a block the current model can't read is always dropped.
40 
41## Keeping the prefix unchanged
42 
43On Claude Fable 5.1, a thinking block stays valid only while everything you sent before it is unchanged on later requests. The checked prefix has three parts:
44 
45* The top-level `system` prompt
46* The set of `tools`
47* Every `message` before the block
48 
49Note: With server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction), the checked prefix starts at the most recent compaction block.
50 
51Request parameters outside those three fields, such as `effort`, `max_tokens`, `output_config`, `tool_choice`, and `metadata`, aren't part of the prefix check, and neither are `cache_control` markers. [What counts as an edit](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#what-counts-as-an-edit) has the full list.
52 
53Earlier thinking blocks aren't in the prefix, but each thinking block records which thinking block came before it, across turns. You can remove thinking blocks from the front of the history, oldest first. Removing one from the middle invalidates thinking blocks after it.
54 
55Keep `system` and `tools` fixed for the session and treat `messages` as append-only. The same discipline keeps the prefix stable for [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching): the edits that invalidate thinking are the edits that restart the cache.
56 
57### What the API does with an invalid block
58 
59You choose with `thinking.block_binding.prefix_mismatch_behavior`:
60 
61* **`"error"` (the default):** the API rejects the request with a 400 `invalid_request_error` that names the first failing block.
62* **`"drop_block"`:** the API drops each failing block and every thinking block after it, and the request succeeds. Dropped blocks aren't billed. The model answers that turn without using reasoning from dropped blocks, and the prompt cache restarts at the edit. The response lists each dropped block in `input_transformations` (on the `message_start` event when streaming) with `reason: "prefix_binding_mismatch"`.
63 
64Both the field and the `input_transformations` array require the `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers). [Set the mismatch behavior and read `input_transformations`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#preserved-thinking-controls) shows the request in each SDK.
65 
66The 400 message begins:
67 
68```text wrap
69messages.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".
70```
71 
72If the request didn't send the beta header, the message continues:
73 
74```text wrap
75That setting requires the `thinking-binding-controls-2026-08-01` value in the `anthropic-beta` header.
76```
77 
78It 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](https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting#error-thinking-block-signature) for every variant of this error.
79 
80If you hit this 400 in production, retrying the same body fails the same way. Retry with the beta header and `prefix_mismatch_behavior: "drop_block"` and keep sending it for the rest of the session, or strip every `thinking` and `redacted_thinking` block from the history yourself and retry once. Then fix the edit that caused the mismatch. In the Message Batches API, an item that leaves the field unset drops failing blocks instead of erroring, so set `"error"` explicitly there if you want batch items to fail.
81 
82A tampered or undecryptable signature is a different failure. It always returns a 400 (``Invalid `signature` in `thinking` block`` with no sentence about the conversation), and `prefix_mismatch_behavior` doesn't apply to it.
83 
84### Set the mismatch behavior and read `input_transformations`
85 
86The `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers) adds:
87 
88* A top-level `input_transformations` array on every response
89* A `block_binding` object on the `thinking` configuration, whose one field is `prefix_mismatch_behavior`
90 
91`block_binding` is accepted alongside `thinking.type: "adaptive"` and `thinking.type: "enabled"`. Sending it without the beta header returns a 400 error. Models that don't run the prefix check accept the object and report only model-check drops, so one request body works across models.
92 
93The following request opts into dropping rather than rejecting. On a first turn there's nothing to replay, so `input_transformations` comes back empty:
94 
95<CodeGroup>
96 ```bash cURL
97 curl https://api.anthropic.com/v1/messages \
98 -H "x-api-key: $ANTHROPIC_API_KEY" \
99 -H "anthropic-version: 2023-06-01" \
100 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
101 -H "content-type: application/json" \
102 -d '{
103 "model": "claude-fable-5-1",
104 "max_tokens": 16000,
105 "thinking": {
106 "type": "adaptive",
107 "block_binding": {
108 "prefix_mismatch_behavior": "drop_block"
109 }
110 },
111 "messages": [
112 {
113 "role": "user",
114 "content": "What is the greatest common divisor of 1071 and 462?"
115 }
116 ]
117 }'
118 ```
119 
120 ```bash CLI
121 ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
122 --transform '{content.#(type=="text")#.text,input_transformations}' \
123 --format yaml <<'YAML'
124 model: claude-fable-5-1
125 max_tokens: 16000
126 thinking:
127 type: adaptive
128 block_binding:
129 prefix_mismatch_behavior: drop_block
130 messages:
131 - role: user
132 content: What is the greatest common divisor of 1071 and 462?
133 YAML
134 ```
135 
136 ```python Python
137 client = anthropic.Anthropic()
138 
139 response = client.beta.messages.create(
140 model="claude-fable-5-1",
141 max_tokens=16000,
142 thinking={
143 "type": "adaptive",
144 "block_binding": {"prefix_mismatch_behavior": "drop_block"},
145 },
146 messages=[
147 {
148 "role": "user",
149 "content": "What is the greatest common divisor of 1071 and 462?",
150 }
151 ],
152 betas=["thinking-binding-controls-2026-08-01"],
153 )
154 
155 for block in response.content:
156 if block.type == "text":
157 print(block.text)
158 
159 print(f"Input transformations: {len(response.input_transformations or [])}")
160 ```
161 
162 ```typescript TypeScript
163 const client = new Anthropic();
164 
165 const response = await client.beta.messages.create({
166 model: "claude-fable-5-1",
167 max_tokens: 16000,
168 thinking: {
169 type: "adaptive",
170 block_binding: { prefix_mismatch_behavior: "drop_block" }
171 },
172 messages: [
173 { role: "user", content: "What is the greatest common divisor of 1071 and 462?" }
174 ],
175 betas: ["thinking-binding-controls-2026-08-01"]
176 });
177 
178 for (const block of response.content) {
179 if (block.type === "text") {
180 console.log(block.text);
181 }
182 }
183 console.log(`Input transformations: ${response.input_transformations?.length ?? 0}`);
184 ```
185 
186 ```csharp C#
187 AnthropicClient client = new();
188 
189 var response = await client.Beta.Messages.Create(
190 new()
191 {
192 Model = "claude-fable-5-1",
193 MaxTokens = 16000,
194 Thinking = new BetaThinkingConfigAdaptive
195 {
196 BlockBinding = new()
197 {
198 PrefixMismatchBehavior = BetaThinkingPrefixMismatchBehavior.DropBlock,
199 },
200 },
201 Messages =
202 [
203 new()
204 {
205 Role = Role.User,
206 Content = "What is the greatest common divisor of 1071 and 462?",
207 },
208 ],
209 Betas = [AnthropicBeta.ThinkingBindingControls2026_08_01],
210 }
211 );
212 
213 foreach (var block in response.Content)
214 {
215 if (block.TryPickText(out var textBlock))
216 {
217 Console.WriteLine(textBlock.Text);
218 }
219 }
220 
221 Console.WriteLine($"Input transformations: {response.InputTransformations?.Count ?? 0}");
222 ```
223 
224 ```go Go
225 client := anthropic.NewClient()
226 
227 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
228 Model: "claude-fable-5-1",
229 MaxTokens: 16000,
230 Thinking: anthropic.BetaThinkingConfigParamUnion{
231 OfAdaptive: &anthropic.BetaThinkingConfigAdaptiveParam{
232 BlockBinding: anthropic.BetaThinkingBlockBindingParam{
233 PrefixMismatchBehavior: anthropic.BetaThinkingPrefixMismatchBehaviorDropBlock,
234 },
235 },
236 },
237 Messages: []anthropic.BetaMessageParam{
238 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What is the greatest common divisor of 1071 and 462?")),
239 },
240 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaThinkingBindingControls2026_08_01},
241 })
242 if err != nil {
243 log.Fatal(err)
244 }
245 
246 for _, block := range response.Content {
247 if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
248 fmt.Println(textBlock.Text)
249 }
250 }
251 fmt.Printf("Input transformations: %d\n", len(response.InputTransformations))
252 ```
253 
254 ```java Java
255 import com.anthropic.models.beta.AnthropicBeta;
256 import com.anthropic.models.beta.messages.BetaMessage;
257 import com.anthropic.models.beta.messages.BetaThinkingBlockBinding;
258 import com.anthropic.models.beta.messages.BetaThinkingConfigAdaptive;
259 import com.anthropic.models.beta.messages.BetaThinkingPrefixMismatchBehavior;
260 import com.anthropic.models.beta.messages.MessageCreateParams;
261 
262 void main() {
263 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
264 
265 MessageCreateParams params = MessageCreateParams.builder()
266 .model("claude-fable-5-1")
267 .maxTokens(16000L)
268 .addBeta(AnthropicBeta.THINKING_BINDING_CONTROLS_2026_08_01)
269 .thinking(BetaThinkingConfigAdaptive.builder()
270 .blockBinding(BetaThinkingBlockBinding.builder()
271 .prefixMismatchBehavior(BetaThinkingPrefixMismatchBehavior.DROP_BLOCK)
272 .build())
273 .build())
274 .addUserMessage("What is the greatest common divisor of 1071 and 462?")
275 .build();
276 
277 BetaMessage response = client.beta().messages().create(params);
278 
279 response.content().stream()
280 .flatMap(block -> block.text().stream())
281 .forEach(textBlock -> IO.println(textBlock.text()));
282 IO.println("Input transformations: "
283 + response.inputTransformations().map(List::size).orElse(0));
284 }
285 ```
286 
287 ```php PHP
288 use Anthropic\Beta\AnthropicBeta;
289 use Anthropic\Beta\Messages\BetaThinkingBlockBinding;
290 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
291 use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior;
292 use Anthropic\Client;
293 
294 $client = new Client();
295 
296 $response = $client->beta->messages->create(
297 model: 'claude-fable-5-1',
298 maxTokens: 16000,
299 thinking: BetaThinkingConfigAdaptive::with(
300 blockBinding: BetaThinkingBlockBinding::with(
301 prefixMismatchBehavior: BetaThinkingPrefixMismatchBehavior::DROP_BLOCK,
302 ),
303 ),
304 messages: [
305 ['role' => 'user', 'content' => 'What is the greatest common divisor of 1071 and 462?'],
306 ],
307 betas: [AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01],
308 );
309 
310 foreach ($response->content as $block) {
311 if ($block->type === 'text') {
312 echo $block->text, PHP_EOL;
313 }
314 }
315 
316 echo 'Input transformations: ', count($response->inputTransformations ?? []), PHP_EOL;
317 ```
318 
319 ```ruby Ruby
320 client = Anthropic::Client.new
321 
322 response = client.beta.messages.create(
323 model: "claude-fable-5-1",
324 max_tokens: 16_000,
325 thinking: {
326 type: "adaptive",
327 block_binding: {prefix_mismatch_behavior: "drop_block"}
328 },
329 messages: [
330 {role: "user", content: "What is the greatest common divisor of 1071 and 462?"}
331 ],
332 betas: [Anthropic::AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01]
333 )
334 
335 response.content.each do |block|
336 puts block.text if block.type == :text
337 end
338 
339 puts "Input transformations: #{response.input_transformations&.length || 0}"
340 ```
341</CodeGroup>
342 
343```text Output wrap
344The greatest common divisor of 1071 and 462 is 21.
345Input transformations: 0
346```
347 
348Under the beta header, every response from a thinking-capable model carries `input_transformations`. It's empty when nothing was dropped. Each entry has `type: "thinking_dropped"`, the `path` of the dropped block (for example `messages.1.content.0`), and a `reason` of `prefix_binding_mismatch` or `model_binding_mismatch` (see [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models)). Ignore entries whose `type` or `reason` you don't recognize, because later checks add values.
349 
350When [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming), the array arrives on the `message` object in the `message_start` event. After a mid-stream server-side fallback, the final `message_delta` event carries it again with the serving model's entries. In a [message batch](https://platform.claude.com/docs/en/build-with-claude/batch-processing), an item whose block fails the prefix check under an explicit `"error"` resolves as `errored`, and an item that leaves the field unset drops the failing blocks instead. The [token counting](https://platform.claude.com/docs/en/build-with-claude/token-counting) endpoint runs the same prefix check and returns the same 400.
351 
352### When the API enforces the check
353 
354The prefix check runs on Claude Fable 5.1 for new accounts.
355 
356* **Accounts created on or after August 31, 2026, 00:00 UTC:** the API checks Claude Fable 5.1 requests and applies `"error"` unless you set `"drop_block"`. The same definition of a new account applies to the Claude API and to cloud platforms.
357* **Older accounts:** the API checks requests that set `prefix_mismatch_behavior`. This parameter opts a request in, so you can see what a new account sees without creating one.
358* **Later models:** every account, on every request.
359 
360To find out which group your account is in, take a Claude Fable 5.1 conversation that contains a thinking block, change something before that block, and send it to Claude Fable 5.1 without the beta header or the `block_binding` field. A 400 response that names the header means your account is enforced by default.
47361 
48362<Note>
49 If you maintain a tool or framework that people run with their own API key, your users on new accounts hit the check before you do: your own key is likely on an older account. Test with `prefix_mismatch_behavior` set so you see what they'll see.
363 If you maintain a tool or framework that people run with their own API key, your users on new accounts hit the check before you do, because your own key is likely on an older account. Test with `prefix_mismatch_behavior` set so you see what they see.
50364</Note>
51365 
52## How to tell whether your integration is impacted
53 
54Capture 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.
55 
56Then confirm against the API. With the `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/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:
57 
58```bash
59curl https://api.anthropic.com/v1/messages \
60 -H "content-type: application/json" \
61 -H "x-api-key: $ANTHROPIC_API_KEY" \
62 -H "anthropic-version: 2023-06-01" \
63 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
64 -d '{
65 "model": "claude-fable-5-1",
66 "max_tokens": 16000,
67 "thinking": {
68 "type": "adaptive",
69 "block_binding": { "prefix_mismatch_behavior": "drop_block" }
366### What counts as an edit
367 
368Each row compares two consecutive requests:
369 
370| Change between requests | Later thinking blocks |
371| ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
372| Append messages at the end | Valid |
373| Add a tool with `defer_loading: true` that nothing has referenced yet | Valid |
374| Remove `thinking` blocks from the start of the history | Valid |
375| Change any request parameter outside `system`, `tools`, and `messages` (`effort`, `max_tokens`, `output_config`, `tool_choice`, `metadata`, and so on) | Valid |
376| Add, move, or remove `cache_control` markers | Valid |
377| A rotating signed URL that returns the same bytes | Valid |
378| Server-side compaction or context editing removes or replaces content | Valid (the check compares what you sent, not the server's edited copy) |
379| A cleared [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) left in place | Valid |
380| Edit, reorder, or delete any earlier `user`, `assistant`, or `system` message | Invalid |
381| Add a text block to an earlier user turn, or remove one you added last time | Invalid |
382| Change the top-level `system` string or blocks | Invalid |
383| Add, remove, rename, or edit a tool in `tools` | Invalid |
384| Remove a `thinking` block from the middle of the history and keep later ones | Invalid for every later thinking block |
385| An image or document URL that returns different bytes on the next request | Invalid |
386| The same turn-scoped message deleted or reworded on a later request | Invalid |
387 
388### Check whether your code edits the prefix
389 
390First, diff what you send. Capture the request bodies your integration sends over a few normal turns, including a compaction or a tool change. For each pair of consecutive requests, compare `system`, `tools`, and the `messages` they share. They should be identical up to the newly appended turns.
391 
392Then confirm against the API. Add the `thinking-binding-controls-2026-08-01` beta header, set `prefix_mismatch_behavior` to `"drop_block"`, and run a normal multi-turn session through your integration on claude-fable-5-1. The following example runs two turns the way your integration should: `messages` only grows, each assistant turn goes back exactly as the API returned it, `thinking` blocks included, and `block_binding` is set on every request. It prints the number of dropped blocks after each turn:
393 
394<CodeGroup>
395 ```bash cURL
396 FIRST=$(curl -s https://api.anthropic.com/v1/messages \
397 -H "content-type: application/json" \
398 -H "x-api-key: $ANTHROPIC_API_KEY" \
399 -H "anthropic-version: 2023-06-01" \
400 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
401 -d '{
402 "model": "claude-fable-5-1",
403 "max_tokens": 16000,
404 "thinking": {
405 "type": "adaptive",
406 "block_binding": { "prefix_mismatch_behavior": "drop_block" }
407 },
408 "messages": [{ "role": "user", "content": "What is 27 * 453?" }]
409 }')
410 echo "$FIRST" | jq '.input_transformations | length'
411 
412 # Turn 2: the assistant turn goes back exactly as returned, then the next user message
413 MESSAGES=$(jq -n --argjson first "$FIRST" '[
414 { role: "user", content: "What is 27 * 453?" },
415 { role: "assistant", content: $first.content },
416 { role: "user", content: "Now divide that result by 3." }
417 ]')
418 
419 jq -n --argjson messages "$MESSAGES" '{
420 model: "claude-fable-5-1",
421 max_tokens: 16000,
422 thinking: {
423 type: "adaptive",
424 block_binding: { prefix_mismatch_behavior: "drop_block" }
70425 },
71 "system": "You are a coding agent.",
72 "messages": [
73 { "role": "user", "content": "Fix the failing test." },
426 messages: $messages
427 }' | curl -s https://api.anthropic.com/v1/messages \
428 -H "content-type: application/json" \
429 -H "x-api-key: $ANTHROPIC_API_KEY" \
430 -H "anthropic-version: 2023-06-01" \
431 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
432 -d @- | jq '.input_transformations | length'
433 ```
434 
435 ```bash CLI
436 FIRST=$(ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
437 --transform content --format json <<'YAML'
438 model: claude-fable-5-1
439 max_tokens: 16000
440 thinking:
441 type: adaptive
442 block_binding:
443 prefix_mismatch_behavior: drop_block
444 messages:
445 - role: user
446 content: What is 27 * 453?
447 YAML
448 )
449 
450 # Turn 2: the assistant turn goes back exactly as returned, then the next user message
451 ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
452 --transform input_transformations --format json <<YAML
453 model: claude-fable-5-1
454 max_tokens: 16000
455 thinking:
456 type: adaptive
457 block_binding:
458 prefix_mismatch_behavior: drop_block
459 messages:
460 - role: user
461 content: What is 27 * 453?
462 - role: assistant
463 content: $(echo "$FIRST" | jq -c .)
464 - role: user
465 content: Now divide that result by 3.
466 YAML
467 ```
468 
469 ```python Python
470 client = anthropic.Anthropic()
471 
472 # messages grows across turns: each assistant turn goes back exactly as returned
473 messages = []
474 for user_turn in ["What is 27 * 453?", "Now divide that result by 3."]:
475 messages.append({"role": "user", "content": user_turn})
476 response = client.beta.messages.create(
477 model="claude-fable-5-1",
478 max_tokens=16000,
479 thinking={
480 "type": "adaptive",
481 "block_binding": {"prefix_mismatch_behavior": "drop_block"},
482 },
483 messages=messages,
484 betas=["thinking-binding-controls-2026-08-01"],
485 )
486 messages.append({"role": "assistant", "content": response.content})
487 print(len(response.input_transformations or []))
488 ```
489 
490 ```typescript TypeScript
491 const client = new Anthropic();
492 
493 // messages grows across turns: each assistant turn goes back exactly as returned
494 const messages: Anthropic.Beta.BetaMessageParam[] = [];
495 for (const userTurn of ["What is 27 * 453?", "Now divide that result by 3."]) {
496 messages.push({ role: "user", content: userTurn });
497 const response = await client.beta.messages.create({
498 model: "claude-fable-5-1",
499 max_tokens: 16000,
500 thinking: {
501 type: "adaptive",
502 block_binding: { prefix_mismatch_behavior: "drop_block" }
503 },
504 messages,
505 betas: ["thinking-binding-controls-2026-08-01"]
506 });
507 messages.push({ role: "assistant", content: response.content });
508 console.log(response.input_transformations?.length ?? 0);
509 }
510 ```
511 
512 ```csharp C#
513 AnthropicClient client = new();
514 
515 // messages grows across turns: each assistant turn goes back exactly as returned
516 List<BetaMessageParam> messages = [];
517 foreach (var userTurn in new[] { "What is 27 * 453?", "Now divide that result by 3." })
518 {
519 messages.Add(new() { Role = Role.User, Content = userTurn });
520 var response = await client.Beta.Messages.Create(
521 new()
522 {
523 Model = "claude-fable-5-1",
524 MaxTokens = 16000,
525 Thinking = new BetaThinkingConfigAdaptive
526 {
527 BlockBinding = new()
528 {
529 PrefixMismatchBehavior = BetaThinkingPrefixMismatchBehavior.DropBlock,
530 },
531 },
532 Messages = messages,
533 Betas = [AnthropicBeta.ThinkingBindingControls2026_08_01],
534 }
535 );
536 messages.Add(new()
74537 {
75 "role": "assistant",
76 "content": [
77 { "type": "thinking", "thinking": "", "signature": "EqQBCkYIBxgCKkD..." },
78 { "type": "text", "text": "I need to see the test first. Which file is it in?" }
79 ]
538 Role = Role.Assistant,
539 Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList(),
540 });
541 Console.WriteLine(response.InputTransformations?.Count ?? 0);
542 }
543 ```
544 
545 ```go Go
546 client := anthropic.NewClient()
547 
548 // messages grows across turns: each assistant turn goes back exactly as returned
549 messages := []anthropic.BetaMessageParam{}
550 for _, userTurn := range []string{"What is 27 * 453?", "Now divide that result by 3."} {
551 messages = append(messages, anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(userTurn)))
552 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
553 Model: "claude-fable-5-1",
554 MaxTokens: 16000,
555 Thinking: anthropic.BetaThinkingConfigParamUnion{
556 OfAdaptive: &anthropic.BetaThinkingConfigAdaptiveParam{
557 BlockBinding: anthropic.BetaThinkingBlockBindingParam{
558 PrefixMismatchBehavior: anthropic.BetaThinkingPrefixMismatchBehaviorDropBlock,
559 },
560 },
561 },
562 Messages: messages,
563 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaThinkingBindingControls2026_08_01},
564 })
565 if err != nil {
566 log.Fatal(err)
567 }
568 messages = append(messages, response.ToParam())
569 fmt.Println(len(response.InputTransformations))
570 }
571 ```
572 
573 ```java Java
574 import com.anthropic.models.beta.AnthropicBeta;
575 import com.anthropic.models.beta.messages.BetaMessage;
576 import com.anthropic.models.beta.messages.BetaThinkingBlockBinding;
577 import com.anthropic.models.beta.messages.BetaThinkingConfigAdaptive;
578 import com.anthropic.models.beta.messages.BetaThinkingPrefixMismatchBehavior;
579 import com.anthropic.models.beta.messages.MessageCreateParams;
580 
581 void main() {
582 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
583 
584 // The builder's message list grows across turns: each assistant turn goes back exactly as returned
585 MessageCreateParams.Builder conversation = MessageCreateParams.builder()
586 .model("claude-fable-5-1")
587 .maxTokens(16000L)
588 .thinking(BetaThinkingConfigAdaptive.builder()
589 .blockBinding(BetaThinkingBlockBinding.builder()
590 .prefixMismatchBehavior(BetaThinkingPrefixMismatchBehavior.DROP_BLOCK)
591 .build())
592 .build())
593 .addBeta(AnthropicBeta.THINKING_BINDING_CONTROLS_2026_08_01);
594 
595 for (String userTurn : List.of("What is 27 * 453?", "Now divide that result by 3.")) {
596 conversation.addUserMessage(userTurn);
597 BetaMessage response = client.beta().messages().create(conversation.build());
598 conversation.addMessage(response);
599 IO.println(response.inputTransformations().map(List::size).orElse(0));
600 }
601 }
602 ```
603 
604 ```php PHP
605 use Anthropic\Beta\AnthropicBeta;
606 use Anthropic\Beta\Messages\BetaThinkingBlockBinding;
607 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
608 use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior;
609 use Anthropic\Client;
610 
611 $client = new Client();
612 
613 // $messages grows across turns: each assistant turn goes back exactly as returned
614 $messages = [];
615 foreach (['What is 27 * 453?', 'Now divide that result by 3.'] as $userTurn) {
616 $messages[] = ['role' => 'user', 'content' => $userTurn];
617 $response = $client->beta->messages->create(
618 model: 'claude-fable-5-1',
619 maxTokens: 16000,
620 thinking: BetaThinkingConfigAdaptive::with(
621 blockBinding: BetaThinkingBlockBinding::with(
622 prefixMismatchBehavior: BetaThinkingPrefixMismatchBehavior::DROP_BLOCK,
623 ),
624 ),
625 messages: $messages,
626 betas: [AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01],
627 );
628 $messages[] = ['role' => 'assistant', 'content' => $response->content];
629 echo count($response->inputTransformations ?? []), PHP_EOL;
630 }
631 ```
632 
633 ```ruby Ruby
634 client = Anthropic::Client.new
635 
636 # messages grows across turns: each assistant turn goes back exactly as returned
637 messages = []
638 ["What is 27 * 453?", "Now divide that result by 3."].each do |user_turn|
639 messages << {role: "user", content: user_turn}
640 response = client.beta.messages.create(
641 model: "claude-fable-5-1",
642 max_tokens: 16_000,
643 thinking: {
644 type: "adaptive",
645 block_binding: {prefix_mismatch_behavior: "drop_block"}
80646 },
81 { "role": "user", "content": "tests/test_auth.py" }
82 ]
83 }'
84```
85 
86Every response then carries a top-level `input_transformations` array. Log it on each turn:
647 messages: messages,
648 betas: [Anthropic::AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01]
649 )
650 messages << {role: "assistant", content: response.content}
651 puts (response.input_transformations || []).length
652 end
653 ```
654</CodeGroup>
655 
656```text Output wrap
6570
6580
659```
660 
661Both turns print `0` because nothing earlier changed. Log `input_transformations` on every turn of your own integration. When the API drops a block, the entry looks like the following:
87662 
88663```json
89664{
from line 672
97672}
98673```
99674 
100* **Empty on every turn:** your integration keeps history intact.
101* **`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.
102* **`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.
103 
104This works from any account, because setting the field opts the request into enforcement. To fail loudly in CI instead, set `"error"`. The 400 begins:
675* **Empty on every turn:** your integration keeps the prefix intact.
676* **`reason: "prefix_binding_mismatch"`:** something before the block at `path` changed since the previous request. Diff `system`, `tools`, and `messages` up to that turn to find it, then find the matching replacement in [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits).
677* **`reason: "model_binding_mismatch"`:** the conversation moved to a model that can't read the earlier model's blocks. This isn't a prefix edit. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
678 
679To fail loudly in CI instead, set `"error"` and treat the 400 described in [What the API does with an invalid block](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#mismatch-behavior) as a test failure.
680 
681## Make changes without editing the prefix
682 
683Each common prefix edit has a replacement that gives the model the same information and leaves earlier bytes unchanged, so later thinking stays valid. Find the edit your code makes today in the first column:
684 
685| Instead of | Use | Beta header |
686| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
687| Rebuilding the top-level `system` prompt | A [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) | None |
688| Injecting a reminder and deleting it on the next request | A [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) (`clear_at: "next_user_message"`) | `mid-conversation-system-clear-at-2026-08-21` |
689| Adding or removing entries in `tools` | [`tool_addition` and `tool_removal` blocks](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#tool-changes) | `mid-conversation-tool-changes-2026-07-01` |
690| Changing top-level `output_config.effort` (restarts the cache, doesn't affect thinking) | A [per-message `output_config`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#effort-changes) | `mid-conversation-output-config-2026-07-01` |
691| Dropping or summarizing old turns on the client | Server-side [compaction or context editing](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#server-side-trimming), or [client-side compaction](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client) that keeps no stale thinking | `compact-2026-01-12` or `context-management-2025-06-27` |
692| An image or document URL whose bytes change between requests | A [`file_id` from the Files API](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#files-by-id), or base64 | None |
693 
694All of these assume you [send assistant turns back exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned). To use several betas in one request, combine the values in one `anthropic-beta` header. The same names apply on Amazon Bedrock and Google Cloud (see [Beta headers](https://platform.claude.com/docs/en/api/beta-headers)):
105695 
106696```text wrap
107messages.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".
108```
109 
110Without 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.
111 
112See [Troubleshooting thinking](https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting#error-thinking-block-signature) for every variant of this error.
113 
114## What counts as an edit
115 
116Between two consecutive requests:
117 
118| Change between requests | Later thinking blocks |
119| ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
120| Append messages at the end | Valid |
121| Add a tool with `defer_loading: true` that nothing has referenced yet | Valid |
122| Remove `thinking` blocks from the start of the history (every thinking block before some point) | Valid |
123| Change any request parameter outside `system`, `tools`, and `messages` (`max_tokens`, `output_config`, `tool_choice`, `metadata`, and so on) | Valid |
124| Add, move, or remove `cache_control` markers | Valid |
125| A rotating signed URL that returns the same bytes | Valid |
126| Server-side compaction or context editing removes or replaces content | Valid (the check compares what you sent, not the server's edited copy) |
127| A cleared [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) left in place | Valid |
128| Edit, reorder, or delete any earlier `user`, `assistant`, or `system` message | Invalid |
129| Add a text block to an earlier user turn, or remove one you added last time | Invalid |
130| Change the top-level `system` string or blocks | Invalid |
131| Add, remove, rename, or edit a tool in `tools` | Invalid |
132| Remove a `thinking` block from the middle of the history and keep later ones | Invalid for every later thinking block |
133| An image or document URL that returns different bytes on the next request | Invalid |
134| The same turn-scoped message deleted or reworded on a later request | Invalid |
135 
136## Update your integration
137 
138Each pattern replaces one kind of history edit with an API feature that has the same effect on the model without changing earlier bytes.
139 
140### Append assistant turns exactly as returned
141 
142Store 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.
143 
144### Add instructions with a mid-conversation system message, not by editing `system`
145 
146If 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](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) at the point in `messages` where it becomes true:
697anthropic-beta: thinking-binding-controls-2026-08-01,mid-conversation-system-clear-at-2026-08-21,mid-conversation-tool-changes-2026-07-01
698```
699 
700### Send assistant turns back exactly as returned
701 
702Store 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. A serializer that drops unknown block types, drops empty fields, or reorders blocks edits the prefix for every later turn.
703 
704### Add instructions with a mid-conversation system message
705 
706Some harnesses rebuild the top-level `system` prompt on each request to carry the current time, a token budget, a mode flag, or newly discovered project context. That invalidates every thinking block in the conversation. Instead, freeze `system` at session start. When something changes, append a [`role: "system"` message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) at the point in `messages` where the change becomes true:
147707 
148708```json
149709{
from line 712
152712}
153713```
154714 
155The 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](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations)).
715The model treats this message with system-prompt authority, and everything before it stays unchanged. In a tool loop, place the message after the `tool_result` user message, never between an assistant `tool_use` and its `tool_result` (see [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations)). Once sent, the message is part of the prefix for later thinking: leave it in place on later requests.
156716 
157717### Send per-turn reminders as turn-scoped system messages
158718 
159The 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.
160 
161Instead, send the nudge as a [mid-conversation system message](https://platform.claude.com/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:
719The most common prefix edit is the per-turn nudge: a line such as "request independent reads together" or "you haven't updated the user in a while" that your code appends after each batch of tool results. To keep reminders from piling up, send each nudge as a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) with `clear_at: "next_user_message"`, placed after the `tool_result` user message. `clear_at` requires the beta header `mid-conversation-system-clear-at-2026-08-21`. The following `messages` array is the request after two tool calls and their results. `messages[3]` is the previous request's nudge, left in place, and `messages[6]` is this request's copy:
162720 
163721```json
164722[
from line 766
208766]
209767```
210768 
211A `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](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages).
212 
213Without 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.
214 
215### Change tools with `tool_addition` and `tool_removal`, not by editing `tools`
216 
217If 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](https://platform.claude.com/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`:
769A user message that contains only `tool_result` blocks counts as the "next user message", so `messages[3]` is already cleared. It adds nothing to what the model sees and costs no input tokens, but because it's still in the array, the thinking in `messages[4]` stays valid. `messages[6]` is the copy the model sees this turn. On later requests, keep both where they are and append a fresh copy after the next `tool_result` message.
770 
771### Add or remove tools with `tool_addition` and `tool_removal`
772 
773Editing the `tools` array mid-session invalidates preserved thinking blocks. Instead, declare every tool the session might need in `tools` on the first request and never change the array. To change which tools the model can use from some point on, append a `role: "system"` message that carries a `tool_removal` or `tool_addition` block. These are [mid-conversation tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes) and need the beta header `mid-conversation-tool-changes-2026-07-01`. For example, to withdraw a dangerous tool after a mode switch:
218774 
219775```json
220776{
from line 782
226782}
227783```
228784 
229A 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.
230 
231### Trim context on the server where you can
232 
233Client-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:
234 
235* [Compaction](https://platform.claude.com/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. Its [`instructions` parameter](https://platform.claude.com/docs/en/build-with-claude/compaction#custom-summarization-instructions) takes your own summarization prompt ("preserve every ticker, position size, and stated assumption").
236* [Context editing](https://platform.claude.com/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.
237 
238### Custom compaction on the client
239 
240This check doesn't prohibit client-side compaction. The rule is narrower: **don't keep a thinking block behind a prefix you've rewritten.**
241 
242**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.
243 
244Two other common shapes fail as written and need one change each:
245 
246* **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: strip `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.
247* **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.
248 
249Snipping 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](https://platform.claude.com/docs/en/build-with-claude/context-editing) for selective removal.
250 
251Don'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](https://platform.claude.com/docs/en/build-with-claude/thinking#preserving-thinking-blocks)).
252 
253### Reference files by ID, not by URL that changes content
254 
255For 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](https://platform.claude.com/docs/en/build-with-claude/files) and use the `file_id`, or send base64.
256 
257### Decide what happens on a mismatch
258 
259Once 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](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback)) is always dropped, and reported in `input_transformations` when the beta header is sent.
260 
261* **`"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.
262* **`"drop_block"`** if you'd rather drop the affected blocks than fail. Log `input_transformations`.
263 
264If 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.
265 
266## API features used on this page
267 
268| Feature | What it replaces | Status | Header |
269| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------ | --------------------------------------------- |
270| [Controls for blocks that aren't preserved](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-thinking-controls) (`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` |
271| [Mid-conversation system messages](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) (`role: "system"` in `messages`) | Rebuilding the top-level `system` prompt | Stable | None |
272| [Turn-scoped system messages](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) (`clear_at: "next_user_message"`) | Injecting a reminder and deleting it next request | Beta | `mid-conversation-system-clear-at-2026-08-21` |
273| [Mid-conversation tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes) (`tool_addition`, `tool_removal`) | Editing the `tools` array | Beta | `mid-conversation-tool-changes-2026-07-01` |
274| [Compaction](https://platform.claude.com/docs/en/build-with-claude/compaction) (`instructions` for a custom summary prompt) | Client-side summarization of old turns | Beta | `compact-2026-01-12` |
275| [Context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) (`clear_tool_uses_20250919`, `clear_thinking_20251015`) | Client-side deletion of old tool results or thinking | Beta | `context-management-2025-06-27` |
276| [Files API](https://platform.claude.com/docs/en/build-with-claude/files) (`file_id` sources) | URLs whose content changes between requests | Stable | None |
277| [Per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta) (`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` |
278 
279To combine headers in one request:
280 
281```text wrap
282anthropic-beta: thinking-binding-controls-2026-08-01,mid-conversation-system-clear-at-2026-08-21,mid-conversation-tool-changes-2026-07-01
283```
284 
285The same beta names apply on Amazon Bedrock and Google Cloud. See [Beta headers](https://platform.claude.com/docs/en/api/beta-headers) for how to send them with each SDK.
286 
287## Checklist
288 
289* If an official Claude product or SDK (Claude Code, claude.ai, Claude Managed Agents, the Claude Agent SDK) manages your conversation history, stop here.
290* Consecutive request bodies are byte-identical in `system`, `tools`, and the shared `messages` prefix.
291* A full session under `prefix_mismatch_behavior: "drop_block"` logs no `prefix_binding_mismatch` entries.
292* Assistant turns go back byte-for-byte as returned, all block types included.
293* Top-level `system` and `tools` are fixed for the session. Changes go in `role: "system"` messages and `tool_addition` / `tool_removal` blocks.
294* Per-turn reminders are turn-scoped system messages (or trailing text blocks) that are appended fresh and never removed.
295* 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.
296* Cross-turn files are `file_id` or base64, not mutable URLs.
297* A production `prefix_mismatch_behavior` is set and its 400s or dropped entries are monitored.
785To offer a tool later instead, declare it in `tools` with `defer_loading: true` so the model doesn't see it at first. When it becomes available, append a `tool_addition` block:
786 
787```json
788{
789 "role": "system",
790 "content": [
791 { "type": "tool_addition", "tool": { "type": "tool_reference", "name": "deploy" } },
792 { "type": "text", "text": "Authentication succeeded. Deployment is now available." }
793 ]
794}
795```
796 
797Sometimes you can't declare a tool up front because you don't know its schema yet. An MCP server discovered at runtime is the common case. Append that tool to `tools` with `defer_loading: true`, then offer it with a `tool_addition` block. Adding a deferred tool is safe: the prefix check ignores a deferred tool until a `tool_addition` block references it, so earlier thinking stays valid. Adding a tool without `defer_loading: true` changes the prefix and invalidates earlier thinking.
798 
799The `role: "system"` messages that carry these blocks join the prefix for later thinking. Leave them in place on later requests.
800 
801### Change effort with a per-message `output_config`
802 
803Changing top-level `output_config.effort` between requests doesn't invalidate thinking, because effort isn't part of the prefix. Changing top-level effort does restart the prompt cache. On Claude Fable 5.1, use [per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta) instead: append a `role: "system"` message with empty `content` and the new level. It needs the beta header `mid-conversation-output-config-2026-07-01`.
804 
805```json
806{ "role": "system", "content": [], "output_config": { "effort": "low" } }
807```
808 
809The new level takes effect from the next `user` turn. Once sent, the message is part of `messages` and therefore part of the prefix for later thinking: leave it in place on later requests, and append another one to change effort again.
810 
811### Trim context on the server
812 
813The second most common prefix edit is client-side trimming: dropping or summarizing the oldest turns and keeping the recent ones verbatim. The kept turns' thinking blocks were produced while the removed history 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:
814 
815* [Compaction](https://platform.claude.com/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. Its [`instructions` parameter](https://platform.claude.com/docs/en/build-with-claude/compaction#custom-summarization-instructions) takes your own summarization prompt, such as "preserve every ticker, position size, and stated assumption".
816* [Context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) clears old tool results or old thinking blocks by rule, oldest first. The strategies are `clear_tool_uses_20250919` and `clear_thinking_20251015`.
817 
818### Compact on the client
819 
820You can still compact on the client. Once you rewrite anything earlier in the conversation, don't send back a thinking block that was produced before the rewrite.
821 
822#### Simple compaction (recommended)
823 
824When the conversation grows too long, summarize the whole session into one user message and send only that message plus the next instruction. Nothing earlier is replayed, so there's no thinking left to fail the check, and the model reasons afresh from the summary.
825 
826![Simple compaction: request 4 sends the full history with thinking on each assistant turn; request 5 sends one user message holding a summary of turns 1 to 4 plus the next instruction, so no earlier thinking is sent and nothing is checked](https://platform.claude.com/docs/images/preserved-thinking-simple-compaction.svg)
827 
828```json
829[
830 {
831 "role": "user",
832 "content": "<summary of the session so far>\n\n<the next instruction>"
833 }
834]
835```
836 
837Claude models are trained on long-horizon tasks with this scheme and for most workloads it performs well.
838 
839#### Keep-tail compaction
840 
841Keep-tail compaction summarizes the older turns and keeps the most recent turns verbatim, so the model still sees the last few exchanges word for word. As usually written it breaks the rule: the kept assistant turns still carry thinking blocks that were produced when the original turns, not the summary, came before them. Those blocks fail.
842 
843![Keep-tail compaction: the history is replaced by a summary of turns 1 and 2 followed by turns 3 to 5 verbatim; the thinking on assistant turns 3 and 4 was produced after the original turns, not the summary, so it fails; the same request sent with prefix\_mismatch\_behavior drop\_block succeeds, the API drops those two blocks and lists them in input\_transformations](https://platform.claude.com/docs/images/preserved-thinking-keep-tail-compaction.svg)
844 
845Fix: keep the turns exactly as they are and send `prefix_mismatch_behavior: "drop_block"`. The API drops the stale thinking blocks, the model reads the kept turns' `text` and `tool_use` blocks, and the request succeeds.
846 
847Pass the compacted history as `messages` and set `block_binding` on the `thinking` configuration. In the following example, `compacted_messages` is the array your compaction step produced: the summary message followed by the kept turns exactly as the API returned them, `thinking` blocks included:
848 
849<CodeGroup>
850 ```bash cURL
851 curl https://api.anthropic.com/v1/messages \
852 -H "content-type: application/json" \
853 -H "x-api-key: $ANTHROPIC_API_KEY" \
854 -H "anthropic-version: 2023-06-01" \
855 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
856 -d "{
857 \"model\": \"claude-fable-5-1\",
858 \"max_tokens\": 16000,
859 \"thinking\": {
860 \"type\": \"adaptive\",
861 \"block_binding\": { \"prefix_mismatch_behavior\": \"drop_block\" }
862 },
863 \"messages\": $COMPACTED_MESSAGES
864 }"
865 ```
866 
867 ```bash CLI
868 ant beta:messages create --beta thinking-binding-controls-2026-08-01 <<YAML
869 model: claude-fable-5-1
870 max_tokens: 16000
871 thinking:
872 type: adaptive
873 block_binding:
874 prefix_mismatch_behavior: drop_block
875 messages: $COMPACTED_MESSAGES
876 YAML
877 ```
878 
879 ```python Python
880 client = anthropic.Anthropic()
881 
882 # compacted_messages: the summary message, then the kept turns as returned
883 response = client.beta.messages.create(
884 model="claude-fable-5-1",
885 max_tokens=16000,
886 thinking={
887 "type": "adaptive",
888 "block_binding": {"prefix_mismatch_behavior": "drop_block"},
889 },
890 messages=compacted_messages,
891 betas=["thinking-binding-controls-2026-08-01"],
892 )
893 
894 print(response.input_transformations)
895 ```
896 
897 ```typescript TypeScript
898 const client = new Anthropic();
899 
900 // compactedMessages: the summary message, then the kept turns as returned
901 const response = await client.beta.messages.create({
902 model: "claude-fable-5-1",
903 max_tokens: 16000,
904 thinking: {
905 type: "adaptive",
906 block_binding: { prefix_mismatch_behavior: "drop_block" }
907 },
908 messages: compactedMessages,
909 betas: ["thinking-binding-controls-2026-08-01"]
910 });
911 
912 console.log(response.input_transformations);
913 ```
914 
915 ```csharp C#
916 AnthropicClient client = new();
917 
918 // compactedMessages: the summary message, then the kept turns as returned
919 var response = await client.Beta.Messages.Create(
920 new()
921 {
922 Model = "claude-fable-5-1",
923 MaxTokens = 16000,
924 Thinking = new BetaThinkingConfigAdaptive
925 {
926 BlockBinding = new()
927 {
928 PrefixMismatchBehavior = BetaThinkingPrefixMismatchBehavior.DropBlock,
929 },
930 },
931 Messages = compactedMessages,
932 Betas = [AnthropicBeta.ThinkingBindingControls2026_08_01],
933 }
934 );
935 
936 Console.WriteLine(response.InputTransformations?.Count ?? 0);
937 ```
938 
939 ```go Go
940 client := anthropic.NewClient()
941 
942 // compactedMessages: the summary message, then the kept turns as returned
943 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
944 Model: "claude-fable-5-1",
945 MaxTokens: 16000,
946 Thinking: anthropic.BetaThinkingConfigParamUnion{
947 OfAdaptive: &anthropic.BetaThinkingConfigAdaptiveParam{
948 BlockBinding: anthropic.BetaThinkingBlockBindingParam{
949 PrefixMismatchBehavior: anthropic.BetaThinkingPrefixMismatchBehaviorDropBlock,
950 },
951 },
952 },
953 Messages: compactedMessages,
954 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaThinkingBindingControls2026_08_01},
955 })
956 if err != nil {
957 log.Fatal(err)
958 }
959 
960 fmt.Println(len(response.InputTransformations))
961 ```
962 
963 ```java Java
964 import com.anthropic.models.beta.AnthropicBeta;
965 import com.anthropic.models.beta.messages.BetaMessage;
966 import com.anthropic.models.beta.messages.BetaThinkingBlockBinding;
967 import com.anthropic.models.beta.messages.BetaThinkingConfigAdaptive;
968 import com.anthropic.models.beta.messages.BetaThinkingPrefixMismatchBehavior;
969 import com.anthropic.models.beta.messages.MessageCreateParams;
970 
971 void main() {
972 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
973 
974 // compactedMessages: the summary message, then the kept turns as returned
975 MessageCreateParams params = MessageCreateParams.builder()
976 .model("claude-fable-5-1")
977 .maxTokens(16000L)
978 .thinking(BetaThinkingConfigAdaptive.builder()
979 .blockBinding(BetaThinkingBlockBinding.builder()
980 .prefixMismatchBehavior(BetaThinkingPrefixMismatchBehavior.DROP_BLOCK)
981 .build())
982 .build())
983 .messages(compactedMessages)
984 .addBeta(AnthropicBeta.THINKING_BINDING_CONTROLS_2026_08_01)
985 .build();
986 
987 BetaMessage response = client.beta().messages().create(params);
988 
989 IO.println(response.inputTransformations());
990 }
991 ```
992 
993 ```php PHP
994 use Anthropic\Beta\AnthropicBeta;
995 use Anthropic\Beta\Messages\BetaThinkingBlockBinding;
996 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
997 use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior;
998 use Anthropic\Client;
999 
1000 $client = new Client();
1001 
1002 // $compactedMessages: the summary message, then the kept turns as returned
1003 $response = $client->beta->messages->create(
1004 model: 'claude-fable-5-1',
1005 maxTokens: 16000,
1006 thinking: BetaThinkingConfigAdaptive::with(
1007 blockBinding: BetaThinkingBlockBinding::with(
1008 prefixMismatchBehavior: BetaThinkingPrefixMismatchBehavior::DROP_BLOCK,
1009 ),
1010 ),
1011 messages: $compactedMessages,
1012 betas: [AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01],
1013 );
1014 
1015 var_dump($response->inputTransformations);
1016 ```
1017 
1018 ```ruby Ruby
1019 client = Anthropic::Client.new
1020 
1021 # compacted_messages: the summary message, then the kept turns as returned
1022 response = client.beta.messages.create(
1023 model: "claude-fable-5-1",
1024 max_tokens: 16_000,
1025 thinking: {
1026 type: "adaptive",
1027 block_binding: {prefix_mismatch_behavior: "drop_block"}
1028 },
1029 messages: compacted_messages,
1030 betas: [Anthropic::AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01]
1031 )
1032 
1033 puts response.input_transformations
1034 ```
1035</CodeGroup>
1036 
1037The response carries the new assistant turn as usual, plus one `input_transformations` entry per dropped block. For the history in the diagram, that's the thinking on assistant turns 3 and 4:
1038 
1039```json
1040{
1041 "input_transformations": [
1042 {
1043 "type": "thinking_dropped",
1044 "path": "messages.2.content.0",
1045 "reason": "prefix_binding_mismatch"
1046 },
1047 {
1048 "type": "thinking_dropped",
1049 "path": "messages.4.content.0",
1050 "reason": "prefix_binding_mismatch"
1051 }
1052 ]
1053}
1054```
1055 
1056Keep sending `"drop_block"` on later requests for as long as those two turns stay in the history. Thinking the model produces from this request onward follows the summary and stays valid. If you'd rather not depend on the beta header, the alternative is to strip the `thinking` and `redacted_thinking` blocks from the kept assistant turns yourself when you build the compacted history.
1057 
1058#### Patterns that don't work with preserved thinking
1059 
1060* **Background compaction.** Building the summary off the critical path and swapping it in a few requests later breaks the rule the same way keep-tail does, with a delay: every assistant turn produced while the summary was being built carries thinking that predates the swap, and it all fails the moment the summary lands. If you need it, treat the swap like keep-tail and send `"drop_block"` from the swap onward. Otherwise compact synchronously.
1061* **Cutting turns out of the middle.** Removing individual turns invalidates every thinking block after them, and no compaction scheme avoids that. If you were cutting a turn to change an instruction, append a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) instead. To remove old tool results or old thinking selectively, use server-side [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing).
1062* **Compacting in the middle of a tool round.** Don't compact between an assistant turn's `tool_use` and the `tool_result` that answers it. Send that assistant turn back with its thinking intact so the model finishes the round with its reasoning. See [Preserving thinking blocks](https://platform.claude.com/docs/en/build-with-claude/thinking#preserving-thinking-blocks).
1063 
1064### Reference files by ID, not by a URL whose content changes
1065 
1066For an `image` or `document` block with a `url` source, the check covers the fetched bytes, not the URL string. A URL whose content changes invalidates later thinking: a "latest screenshot" endpoint, or a document someone edits between turns. A rotating signed URL for the same file doesn't. For content you reference across turns, upload it once with the [Files API](https://platform.claude.com/docs/en/build-with-claude/files) and use the `file_id`, or send base64.
2981067 
2991068## FAQ
3001069 
3011070<AccordionGroup>
302 <Accordion title="Do I need a new account to test the check?">
303 No. Send the `thinking-binding-controls-2026-08-01` beta header and set `thinking.block_binding.prefix_mismatch_behavior`. Setting the field opts that request into enforcement regardless of account age: `"error"` rejects an edited history with the same 400 a new account gets, and `"drop_block"` lets the request through and lists what was dropped in `input_transformations`. See [How to tell whether your integration is impacted](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-to-tell-whether-your-integration-is-impacted).
1071 <Accordion title="Do I need a new account to test preserved thinking?">
1072 No. Send the `thinking-binding-controls-2026-08-01` beta header and set `thinking.block_binding.prefix_mismatch_behavior`. Setting the field opts that request into enforcement regardless of account age. `"error"` rejects an edited history with the same 400 a new account gets, and `"drop_block"` lets the request through and lists what was dropped in `input_transformations`. See [Check whether your code edits the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-to-tell-whether-your-integration-is-impacted).
3041073 </Accordion>
3051074 
3061075 <Accordion title="If anything before a thinking block changes, even one tool description, is the conversation unusable?">
307 No. What fails is the thinking already in the history after the point you changed, and you choose what happens to it. With `prefix_mismatch_behavior: "drop_block"` the API drops those blocks and the request succeeds: the model answers that turn without that reasoning, and the prompt cache restarts at the edit. With the default `"error"` the API rejects the request with a 400 until you undo the edit or resend with `"drop_block"`; see [Decide what happens on a mismatch](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#decide-what-happens-on-a-mismatch). [What counts as an edit](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#what-counts-as-an-edit) lists which changes matter.
1076 No. What fails is the thinking already in the history after the point you changed, and you choose what happens to it. With `prefix_mismatch_behavior: "drop_block"`, the API drops those blocks and the request succeeds: the model answers that turn without that reasoning, and the prompt cache restarts at the edit. With the default `"error"`, the API rejects the request with a 400 until you undo the edit or resend with `"drop_block"`. See [What the API does with an invalid block](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#mismatch-behavior). [What counts as an edit](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#what-counts-as-an-edit) lists which changes matter.
3081077 </Accordion>
3091078 
3101079 <Accordion title="Does changing effort or other thinking settings between requests invalidate earlier thinking?">
311 No. `output_config.effort`, `max_tokens`, and the `thinking` configuration aren't part of the checked prefix, which covers only `system`, `tools`, and `messages`. A top-level effort change still invalidates most of the prompt cache; on Claude Fable 5.1, a [per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta) change keeps it. Once sent, that effort message is part of the history: leave it in place on later requests.
1080 No. `output_config.effort`, `max_tokens`, and the `thinking` configuration aren't part of the checked prefix, which covers only `system`, `tools`, and `messages`. A top-level effort change invalidates most of the prompt cache. On Claude Fable 5.1, a [per-message effort](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#effort-changes) change keeps the prompt cache and is used as the new effort level until changed again.
3121081 </Accordion>
3131082 
3141083 <Accordion title="My tool list changes mid-session. How do I avoid invalidating the conversation?">
315 Don't edit `tools`: declare the full set at session start, mark tools that aren't available yet `defer_loading: true`, and offer or withdraw them with `tool_addition` and `tool_removal` blocks. A tool whose schema you learn only mid-session, such as one from an MCP server discovered at runtime, can still be appended to `tools` with `defer_loading: true` and offered the same way, because an unreferenced deferred tool isn't part of the prefix. The `role: "system"` messages that carry these blocks join the prefix for later thinking, so don't move, reword, or delete them afterward. See [Change tools with `tool_addition` and `tool_removal`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#change-tools-with-tool-addition-and-tool-removal-not-by-editing-tools).
1084 Don't edit `tools`. Declare the full set at session start, mark tools that aren't available yet with `defer_loading: true`, and offer or withdraw them with `tool_addition` and `tool_removal` blocks. If you learn a tool's schema only mid-session, such as from an MCP server discovered at runtime, you can still append it to `tools` with `defer_loading: true` and offer it the same way. That's safe because an unreferenced deferred tool isn't part of the prefix. The `role: "system"` messages that carry these blocks join the prefix for later thinking, so don't move, reword, or delete them afterward. See [Add or remove tools with `tool_addition` and `tool_removal`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#tool-changes).
3161085 </Accordion>
3171086 
3181087 <Accordion title="I compact by summarizing older turns and keeping recent turns verbatim. Does that still work?">
319 Not if the kept turns still carry their thinking: those blocks were produced against the history you replaced, so they fail the check. Strip `thinking` and `redacted_thinking` from the turns you carry across (their `text` and `tool_use` blocks stay), or send `prefix_mismatch_behavior: "drop_block"` and let the API drop them. Simple compaction (one summary message plus the next user turn, no earlier turns replayed) leaves no thinking behind to fail and is the recommended shape. Server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction) and [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) don't count as edits. See [Custom compaction on the client](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client).
1088 Not if the kept turns still carry their thinking: those blocks were produced against the history you replaced, so they fail the check. Strip `thinking` and `redacted_thinking` blocks from the turns you carry across and keep their `text` and `tool_use` blocks, or send `prefix_mismatch_behavior: "drop_block"` and let the API drop them. Simple compaction leaves no thinking behind to fail and is the recommended approach: one summary message plus the next user turn, with no earlier turns replayed. Server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction) and [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) don't count as edits. See [Compact on the client](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client).
3201089 </Accordion>
3211090 
3221091 <Accordion title="How do I handle instruction files such as AGENTS.md or CLAUDE.md that change mid-session?">
323 Load them once at session start and keep the top-level `system` prompt and `tools` fixed. When a file changes, append the new version at that point in `messages` instead of editing the original: a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) for instructions that come from you as the operator, or content in the next `user` turn for file text you treat as untrusted, which shouldn't carry system-prompt authority. See [Add instructions with a mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#add-instructions-with-a-mid-conversation-system-message-not-by-editing-system) and [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations).
1092 Load them once at session start and keep the top-level `system` prompt and `tools` fixed. When a file changes, append the new version at that point in `messages` instead of editing the original. Use a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) for instructions that come from you as the operator. For file text you treat as untrusted, which shouldn't carry system-prompt authority, put the content in the next `user` turn instead. See [Add instructions with a mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) and [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations).
3241093 </Accordion>
3251094 
3261095 <Accordion title="Can I resume a saved session later, after a restart or the next day?">
327 Yes. A resumed session is an ordinary follow-up request: `system`, `tools`, and the earlier `messages` must match what you last sent byte-for-byte. Persist what you sent and received (the rendered system prompt, the tool definitions, each assistant turn as returned) and replay that, rather than re-rendering from inputs that might have changed since, such as the date, an updated instruction file, or a new tool version. Anything new goes in an appended message. See [Append assistant turns exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned).
328 </Accordion>
329 
330 <Accordion title="What happens to thinking when the conversation moves from Claude Fable 5.1 to an older model and back?">
331 Keep sending the full history and let the API decide on each request. An older model can't read Claude Fable 5.1's thinking blocks, so the API leaves them out of what that model sees for that one request (no error, not billed, and reported as `model_binding_mismatch` in `input_transformations` when you send the beta header); it never edits your `messages` array, so the blocks stay in your history. When the same history goes back to Claude Fable 5.1, those blocks are readable again, along with the older model's thinking. The reasoning is lost only if your client removes the blocks itself, for example a harness that strips thinking on a model switch or rebuilds the history from what each model used. See [Only for the model that produced it, or a newer one](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-for-model) for which models read which blocks.
332 
333 ![Animation: switching to Claude Opus skips Claude Fable 5.1's thinking for that turn; switching back, everything is read again](https://platform.claude.com/docs/images/preserved-thinking-model-switch.gif)
1096 Yes. A resumed session is an ordinary follow-up request: `system`, `tools`, and the earlier `messages` must match what you last sent byte-for-byte. Persist exactly what you sent and received, and replay that: the rendered system prompt, the tool definitions, and each assistant turn as returned. Don't re-render from inputs that might have changed since, such as the date, an updated instruction file, or a new tool version. Anything new goes in an appended message. See [Send assistant turns back exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned).
3341097 </Accordion>
3351098 
3361099 <Accordion title="My harness can route a turn to a non-Claude model. Do those turns invalidate Claude's earlier thinking?">
from line 1101
3381101 </Accordion>
3391102 
3401103 <Accordion title="Can I carry a conversation's reasoning into a new conversation?">
341 Not into a different conversation. A thinking block is usable only behind the exact `system`, `tools`, and `messages` it was produced from, so a branch that replays that history unchanged up to the fork point keeps its thinking, and a conversation that starts from anything else can't use it. Start that one from a summary of the task state (the goal, decisions made, files and results so far, and the next step), as in [simple compaction](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client).
1104 Not into a different conversation. A thinking block is usable only when it follows the exact `system`, `tools`, and `messages` it was produced from. A branch that replays that history unchanged up to the fork point keeps its thinking. A conversation that starts from anything else can't use it, so start that conversation from a summary of the task state, as in [simple compaction](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client): the goal, decisions made, files and results so far, and the next step.
3421105 </Accordion>
3431106</AccordionGroup>
3441107 
3451108