Prompt caching, from the causal mask outwardPart 2 of 4
Where the breakpoint goes
The placement that bills more than no caching, why the last block suits a growing loop, and how four breakpoints and the cascade divide a prompt.
ContentsPart 2
The placement that costs more than no caching at all
Now the failure. Static context in the first few blocks, a per-request block carrying a timestamp at the end, breakpoint on the last block. Every request produces a different hash there. The walk steps back through the earlier blocks and finds nothing — because no request ever wrote at those positions. So you pay a fresh cache write every single time and never once read.
Surely the backward walk saves me here? No, and this is the crux of the whole mechanism. The walk looks for writes. Those earlier blocks are stable, but stability is not the criterion — no request ever placed a breakpoint there, so there is nothing to find. The fix is to move the breakpoint to the last block that is byte-identical across requests, not to hope the fallback covers you.
Automatic caching falls into exactly this trap when the prompt ends in unique per-request content, because it targets the last block. The signature in the usage fields is unmistakable once you know it: cache_creation_input_tokens on every request while cache_read_input_tokens never covers the shared prefix.
Move the breakpoint below and watch the arithmetic. The document here is 18,000 tokens; the timestamp that ruins it is forty.
Two things in there are worth sitting with. The pathological placement is more expensive than no caching — not merely wasteful. And nothing in the response says so: no error, no warning, no field that turns red. Just a bill.
When “the last block” is exactly right
A growing conversation is the case automatic caching was built for. Earlier turns never change, so each request’s lookback finds the entry the previous request wrote a few positions back. The breakpoint rolls forward and the cached region ratchets.
That gives you the signature of a loop that is working, which is worth memorising because it is how you tell a real hit from a coincidence:
cache_read_input_tokens— the whole prior prefix, growing every turn.cache_creation_input_tokens— roughly the last assistant output plus the newly appended input. Small.input_tokens— just the tail after the last breakpoint.
The catch is the twenty-position lookback. Each turn in the diagram adds two positions, so turn three easily reaches turn two’s write. An agent loop that appends a dozen positions per iteration — long sequential tool chains, many text or image blocks — will eventually push the breakpoint more than twenty positions past the last write, and then it falls off a cliff: full miss, full rewrite, byte-identical payloads, no warning. Place an intermediate breakpoint roughly every fifteen positions in long turns, so a write has accumulated closer in before you need it.
Four breakpoints, four clocks
The reason for multiple breakpoints is not granularity for its own sake. You place them at change-frequency boundaries, and four is enough because prompts rarely have more than four distinct rates of change:
tools ← breakpoint 1 changes on deploy
system: rules ← breakpoint 2 changes on deploy
system: docs ← breakpoint 3 changes daily
last user turn ← breakpoint 4 changes every turnSwap the documents and breakpoints 1 and 2 still hit; only 3 and 4 are rewritten. That is the entire point of the segmentation, and it is why a RAG agent that reindexes nightly should never put its retrieved rows ahead of its house rules.
The robust default for an agent loop is a combination: one explicit breakpoint on the last block of the static system prefix — so the expensive shared part has a guaranteed read point that survives whatever happens later in messages — plus top-level automatic caching for the growing conversation tail.
The cascade, and the doors out of it
Because the prefix is ordered tools → system → messages, a change at one tier invalidates that tier and everything after it. Nothing before it. That single sentence is the whole table, but the table is worth having in front of you, because the consequences are asymmetric in ways that are easy to get backwards.
Three of those rows now have escape hatches, and they are the most useful recent development in this whole area, because each one converts a prefix rewrite into an append.
The one to reach for first: stop editing the top-level system prompt mid-conversation. An operator instruction that arrives on turn nine — a mode switch, injected state, a policy change — used to mean rewriting the block that sits in front of the entire conversation. Send it as a message instead:
await client.messages.create({
model: 'claude-opus-5',
max_tokens: 16_000,
system: [
{ type: 'text', text: STABLE_CORE,
cache_control: { type: 'ephemeral' } }, // byte-identical, still hits
],
messages: [
...history,
{ role: 'user', content: userMessage },
{ role: 'system', // ← after the cached prefix
content: 'Terse mode enabled — keep responses under 40 words.' },
],
})Available today with no beta header on Claude Opus 5, Opus 4.8, Fable 5 and 5.1, and Mythos 5 and 5.1 — not on Claude Sonnet 5, where it returns a 400 you should catch and fall back from. It has a second property worth as much as the caching one: a role: "system" message is a channel nothing else can write to, whereas operator instructions embedded as text inside a user turn can be forged by anything that reaches user input.
It must follow a user message (or an assistant message ending in server-tool use), must be either last in messages or followed by an assistant turn, and cannot be messages[0] — the initial prompt still belongs in top-level system.
What about the inject-a-reminder-then-delete-it pattern? It is a history edit, and it costs you the cache from that position on every single turn — plus, on Fable 5.1 and Mythos 5.1, it invalidates every later thinking block. Give the message clear_at: "next_user_message" instead (beta mid-conversation-system-clear-at-2026-08-21): it renders for one turn, then stays in the transcript cleared — costing no input tokens, still part of the prefix. Append a fresh copy each turn and never remove the old ones.
The row with no door is the model switch. Caches are model-scoped, so a cost cascade that routes easy turns to a cheaper model forfeits cache reuse across its lanes — each model is its own namespace. Measure the simpler alternative first: the better model at lower effort, on one cache.
One more that catches people building agents: a fork must reuse the parent’s exact prefix. Summarizers, compactors and sub-agents usually spin up a separate call, and if that call rebuilds system, tools or model with any difference at all, it misses the parent’s cache entirely. Copy them verbatim and append the fork-specific content at the end.