Yesterday I switched the effort level in the middle of a Claude Code session, something I do without thinking, and for once read the dialog that came up.
This conversation is cached for the current effort level. Switching to medium means the full history gets re-read on your next message.
The warning is real. I accepted, and /cost reported 22.6k tokens re-cached on the next turn. Scale it up and the math turns ugly. A 200k-token conversation on Fable 5.1 re-caches at $20 per million tokens on the 1-hour cache tier Claude Code uses, so one careless effort flip costs about $4. The warm turn it replaced would have cost five cents.
What the dialog does not say is that this cost was fixed upstream three days ago, in the Fable 5.1 release. The fix is documented, it works over the API, and Claude Code was already carrying the client code for it behind a feature flag. Then, while I was writing this post, the flag flipped and the dialog vanished from my machine. This is the paper trail of both days.
Effort is rendered into the prompt, so changing top-level output_config.effort restarts the message cache. Fable 5.1, Mythos 5.1 and Opus 5 instead accept an empty system message inside messages that carries only the new effort. Everything before it stays byte-identical and the cache survives. Claude Code shipped the client machinery for this dark, in binaries since at least v2.1.246, and the flag flipped on September 4 alongside v2.1.260. The warning dialog above is already gone.
Why a Settings Change Re-Reads 200k Tokens
Prompt caching only ever matches a byte-identical prefix, in render order, tools first, then system, then messages. I covered the mechanics in July. What I missed back then is where the effort setting lives. It looks like request metadata and behaves like prompt content. The caching docs spell it out.
The thinking configuration and the resolved effort level are rendered into the prompt itself, so changing any of them starts a new cache prefix.
So output_config.effort is part of the cached bytes. The same page has a dedicated invalidation entry for it. Changing the value between requests always invalidates every message block. Tools and system usually survive. The conversation history, which is the expensive part of any agent session, does not.
The price asymmetry is what makes this hurt. Fable 5.1 reads cache at $0.25 per million tokens and re-writes the 1-hour cache at $20 per million, 80 times more. On a subscription you pay in weekly quota instead of dollars, which is presumably why Claude Code bothers to warn you at all.
The Fix Is an Empty Message
Fable 5.1, Mythos 5.1 and Opus 5 now accept a message like this anywhere in the middle of a conversation, under the beta header mid-conversation-output-config-2026-07-01.
{"role": "system", "content": [], "output_config": {"effort": "max"}}
It renders nothing at its position. It is exempt from the placement rules that govern normal mid-conversation system messages, so it can sit anywhere in messages, even first. Effort changes from the next turn onward. And because the change is an appended message rather than an edited request parameter, every byte before it is untouched and the cache hits. The effort docs are blunt about the intent.
On Claude Fable 5.1, Claude Mythos 5.1, and Claude Opus 5, use a per-message effort change, which keeps the prompt cache. On other models, set a new top-level value on the next request, which starts the cache over.
Fable 5 rejects the message with a 400. There is a worked example in the effort docs, and the API release notes date the feature September 1, 2026, the same release that cut Fable cache reads to $0.25 per million.
The design is more interesting than the discount. Effort did not get excluded from the cache key, which would have been the obvious hack. It moved into the conversation, where changing it is an append. Appends are the one operation prefix caching forgives.
Claude Code Ships the Client for This, Dark
Claude Code keeps old binaries around, so I had seven versions sitting in ~/.local/share/claude/versions, 2.1.246 through 2.1.259. I grepped them all. Every one carries the machinery, and no entry in the changelog through 2.1.259 mentions it.
T0 = Ee("per_message_effort", "per-turn-control-2026-07-01")
Internally the feature is called per_message_effort and rides a broader beta header than the documented one. The pieces around it are thorough. Effort is pinned per user message in a map called perTurnEffortPins, capped at 10,000 entries. A walker rebuilds the outgoing request and splices an effort-only system entry at each point where the pinned effort changes. The client folds per-message effort into its own cache-prediction hash so its hit-rate bookkeeping stays correct. And when a model rejects the parameter, it latches that model as unsupported and retries without it, leaving this in the debug log.
[effort] model X rejected output_config.effort; latching unsupported and retrying without it.
Whether any of this runs is decided by a model capability flag named per_turn_effort, delivered at runtime. On my machine, on September 3 with 2.1.259, it was off. Every effort transition I tried in the picker, up or down, produced the same warning dialog, and the switch I accepted re-cached tokens as promised. On September 4, after 2.1.260 auto-installed, I ran the identical experiment. Three warm turns at high effort, then the same high-to-medium switch. No dialog. The transcript shows turn four running at medium, the cache read depth unchanged at 30,683 tokens, and cache writes at the same baseline as the turns before the flip. Yesterday the harness believed a switch meant a full re-read. Today it appends a message and moves on.
The two test runs
| Date and version | Observed switch | What the run recorded |
|---|---|---|
Sep 32.1.259 |
high → medium warning shown |
/cost reported 1 miss and 22.6k tokens re-cached |
Sep 42.1.260 |
high → medium no warning |
turns 3 and 4 both read 30,683 cached tokens; effort changed to medium |
Both were scratch Claude Code sessions that answered with two-token replies. I read cache_read_input_tokens, cache_creation_input_tokens, and effort from the session JSONL after each run. On September 4, cache creation moved from 24,268 tokens before the switch to 24,513 after it, the same small per-turn growth seen in the warm-up turns.
The same 2.1.260 release, incidentally, fixed the other Fable 5.1 caching problem, the one behind this week's limit-burn complaints. Its release note reads "Fixed prompt caching on Claude Fable 5.1 not covering the context attached after tool results, so it was re-sent as uncached input on every tool-call turn." Different bug, same lesson. When cache behavior goes wrong, you pay for it invisibly, at full price, on every single turn.
One measurement caveat, since this blog runs on receipts. My scratch sessions carry roughly 24k tokens of per-turn cache churn from injected context even when nothing changes, so the absolute numbers above are noisy. The comparative signal is clean. Read depth identical before and after the flip, writes at baseline. I also still owe this post a two-request proof against the raw API, which stalled when my personal API key turned out to have expired. For the fix itself, the doc lines quoted above carry the claim.
The Request Envelope Is Dissolving
Per-message effort is the third member of a family. Mid-conversation system messages arrived May 28 with Opus 4.8. Mid-conversation tool changes went to beta July 24. Per-message effort landed September 1. Things that used to be request-level configuration, and used to break the cached prefix when touched, are becoming conversation content. If you build agent harnesses, put mutable controls in appended messages so the old prefix stays cacheable.
I expected to end this post with "the open question is when the flag flips." It flipped mid-draft, about 24 hours after I first read that warning. The dialog is gone, and switching effort no longer forces Claude Code to rewrite the cached history. Everyone who never read the warning will never know it existed, or what it used to cost. A strange way to ship one of the better API design changes of the year, and why I wrote it down.
References
- Anthropic Prompt Caching Docs (see the effort and thinking invalidation entries)
- Anthropic Effort Docs
- Per-Message Effort Worked Example
- Mid-Conversation System Messages
- Claude API Release Notes
- Claude Code 2.1.260 Release
- Claude Code Changelog (all versions)