October 28, 2026
I’m running a build that predates both the bug and the tool that would show me the bug
I was reading the Claude Code changelog the way you do when you’re avoiding something else, and one line in v2.1.248 stopped me:

By Full Expert Developer
4 min read
Fixed a prompt-cache miss (and lost extended-thinking context) roughly once an hour in long sessions, caused by tool definitions being re-rendered after an OAuth token refresh.
Read quickly, that's a maintenance note. Some auth token expires, a bit of internal state gets rebuilt behind it, and a cache entry falls over. Fine.
Then I looked up what "tool definitions" means for a cache, and it stopped being a maintenance note.
Why "tool definitions" is the worst thing on that list
The prompt caching docs publish the cache hierarchy plainly: tools → system → messages. Changes at each level invalidate that level and everything after it.
Tool definitions sit at the top. The docs are unambiguous about what happens when they change: it invalidates the entire cache, rather than just the message blocks or the tail of the conversation.
So the failure isn't "you lost a bit of your discount on the last few turns." An OAuth token refresh in a long session re-rendered the block that sits above everything else, and the next request re-processed the whole prefix from the beginning. In a session that's been open all afternoon, that prefix is the afternoon.
The pricing makes the shape of it clearer. A cache read costs 0.1× the base input rate, while writing that same content back on the one-hour TTL costs 2×. On Opus 5 at $5 per million input tokens, that's fifty cents per million to read from cache against ten dollars per million to write it. Every hourly rebuild moved a chunk of tokens from the first number to the second.
The reason nobody filed this bug
Every individual request succeeded. That's what makes this class of thing invisible.
Cache statistics do come back from the API — the cache read and write token counts sit in the response. But nothing was aggregating them per session and telling you the ratio had collapsed, so unless you were independently logging response metadata and plotting it, an hourly full rebuild looked exactly like normal operation: a slightly slower first token, and otherwise no error and no warning of any kind.
Three releases after the fix, v2.1.251 added a prompt-cache line to the session block. It is the first thing that would have made any of this visible. From the docs, this is the format:
Prompt cache (main): 14 requests · 91% of input tokens from cache · 2 misses
(last 6m 10s ago, 310.2k tokens re-cached) · 1 expected rebuild · warm (1h TTL)Prompt cache (main): 14 requests · 91% of input tokens from cache · 2 misses
(last 6m 10s ago, 310.2k tokens re-cached) · 1 expected rebuild · warm (1h TTL)There's a real definition behind that miss count, which I like more than I expected to: a request counts as a miss when it re-processed more than 5% and at least 2,000 tokens of what it could have read from cache. Two thresholds, both stated, so the number means something specific rather than being a vibe.
It also separates misses from expected rebuilds — compaction and tool-result clearing rewrite the conversation on purpose, and lumping those in with real misses would have made the metric useless within a day.
One limit on it worth knowing: the line covers the main conversation only, and not subagents. If you lean on subagents for the verbose work, which is the advice the cost docs give, a meaningful share of your token traffic sits outside the thing measuring cache health.
I can't tell you whether it was happening to me
I ran claude --version while writing this. This machine is on 2.1.169.
That build predates the fix in 2.1.248, and the meter in 2.1.251 along with it. So I'm running a version that may well have been rebuilding its cache on the hour, with no instrument aboard that could tell me either way, and I cannot resolve it from here.
I'm not going to claim it cost me money, because it didn't. I'm on a subscription rather than an API key, and the docs are explicit that the session cost figure carries no billing meaning for Pro and Max subscribers. On a subscription an hourly rebuild eats plan allowance instead — the same allowance behind the "you've hit your session limit" message. The docs even flag it: the usage breakdown surfaces behaviour flags for things like long context or cache misses when one accounts for 10% or more of recent usage.
There's a second edge here that compounds it. The cache lifetime is an hour on a subscription, and it drops to five minutes the moment you start drawing on usage credits. So the people most likely to be pushed past their allowance are the same people whose cache then expires twelve times faster, and a bug that rebuilds on the hour lands on top of that.
So the honest version is that this may have been shortening my sessions for some unknown stretch of time, and I put it down to using the thing a lot.
And that's the bit I keep turning over. The fix and the first instrument capable of showing you the problem arrived three releases apart, near enough together that for however long this had been going on, nobody outside Anthropic was in a position to notice it.
Not a power user, not someone with a suspicion and a spreadsheet. The evidence didn't exist on your machine.
The case I keep making against my own annoyance
The case for Anthropic is stronger than the setup I've just written implies.
Shipping the fix before the meter is the correct order. The alternative is holding a known bug while you build observability for it, which nobody would defend. And the changelog line is genuinely good disclosure — it names the cause, the frequency, and the second symptom of lost extended-thinking context, in a plain sentence, in public. Most vendors write "improved reliability" and move on.
The bug is also just hard. A token refresh triggering a re-render of tool definitions is exactly the kind of interaction that no unit test is shaped to catch, because both halves work correctly in isolation.
What I'd actually take from this
Nobody behaved badly here, which is what makes it worth writing down. A lot of people can spend a long time optimising against published advice about what invalidates a cache, while the biggest source of invalidation in their sessions never appears on that list at all, because a bug is not a behaviour anyone can change and nothing was exposing this one.
So the thing I'd do differently: when a vendor ships a meter, read the first number it gives you as a statement about the period before you had it. The instrument arriving is itself information about what was previously unmeasurable.
I should probably also update Claude Code. I've been running a build from a while back on the reasonable-sounding theory that it works fine, which is precisely the theory this bug was designed to survive.