[BUG] Conversation history invalidated on subsequent turns
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
While investigating huge token usage I've noticed it come due to fact suddenly my conversation history gets invalidated and all subsequent turns revert to only caching system prompt and huge cache writes.
What Should Happen?
Cache shouldn't drop due to history changes. History should not be updated. Or we shouldn't be charged for historical updates.
Error Messages/Logs
Analysis of token usage from the start of my analysis:
time cache_read cache_cr input out model stop
---------- ---------- ---------- ------- ----- ------------------ ------------
22:22:48 312377 1944 1 215 opus-4-6 end_turn
22:23:39 314321 493 3 159 opus-4-6 end_turn
22:24:19 314814 172 3 108 opus-4-6 end_turn
22:33:42 0 0 8 1 haiku-4-5-20251001 max_tokens <-- resume
22:34:26 11428 305735 3 213 opus-4-6 tool_use <-- irrelevant cache rewrite after restart
22:34:35 317163 579 1 239 opus-4-6 tool_use
22:34:43 317742 566 1 152 opus-4-6 end_turn
22:37:13 318308 245 3 96 opus-4-6 end_turn
07:55:55 0 0 8 1 haiku-4-5-20251001 max_tokens <-- resume
07:56:22 11428 163547 3 143 opus-4-6 tool_use <-- partial cache regenerate (wth?)
07:56:40 174975 358 1 90 opus-4-6 end_turn
07:57:25 11428 307626 3 87 opus-4-6 end_turn <-- full cache regenerate
07:57:51 319054 108 3 89 opus-4-6 tool_use
07:58:05 319162 712 1 448 opus-4-6 tool_use
07:58:21 319874 833 1 367 opus-4-6 end_turn
07:59:21 320707 393 3 414 opus-4-6 tool_use
07:59:34 321100 609 1 560 opus-4-6 tool_use
07:59:47 321709 948 1 512 opus-4-6 tool_use
08:00:10 322657 615 1 348 opus-4-6 end_turn
08:03:00 323272 426 3 530 opus-4-6 tool_use
08:03:12 323698 972 1 468 opus-4-6 tool_use
08:03:22 324670 529 1 167 opus-4-6 end_turn
08:03:29 325199 215 3 28 opus-4-6 end_turn
08:05:30 0 0 8 1 haiku-4-5-20251001 max_tokens <-- resume
08:05:48 11428 187695 3 155 opus-4-6 tool_use
08:06:06 199123 876 1 780 opus-4-6 tool_use
08:06:25 199999 2199 1 1285 opus-4-6 tool_use
08:06:38 202198 1633 1 302 opus-4-6 end_turn
08:08:06 203831 481 3 88 opus-4-6 tool_use
08:08:17 204312 408 1 175 opus-4-6 end_turn
08:09:16 204720 206 3 154 opus-4-6 end_turn
08:10:25 204926 228 3 503 opus-4-6 tool_use
08:10:34 205154 1193 1 507 opus-4-6 tool_use
08:10:45 206347 1007 1 247 opus-4-6 end_turn
08:10:54 0 0 8 1 haiku-4-5-20251001 max_tokens <-- resume
08:11:16 11428 195983 3 136 opus-4-6 tool_use
08:11:37 207411 616 1 1207 opus-4-6 tool_use
08:11:49 208027 1457 1 290 opus-4-6 end_turn
08:12:02 209484 323 3 270 opus-4-6 end_turn
08:12:27 209807 284 3 190 opus-4-6 tool_use
08:12:39 210091 314 1 278 opus-4-6 tool_use
08:13:01 210405 728 1 1219 opus-4-6 tool_use
08:13:18 211133 1465 1 449 opus-4-6 end_turn
08:15:28 212598 599 3 325 opus-4-6 end_turn
08:16:26 213197 334 3 209 opus-4-6 end_turn
08:18:00 213531 288 3 137 opus-4-6 tool_use
08:18:07 213819 1131 1 122 opus-4-6 tool_use
08:18:15 214950 140 1 193 opus-4-6 tool_use
08:18:29 215090 1114 1 269 opus-4-6 tool_use
08:18:54 216204 10504 1 336 opus-4-6 tool_use <-- cache starts breaking down due to history change*
08:19:07 216204 11815 1 228 opus-4-6 tool_use
08:19:17 216204 12990 1 134 opus-4-6 tool_use
08:19:38 216204 13341 1 301 opus-4-6 tool_use
08:20:04 216204 13758 1 426 opus-4-6 tool_use
08:20:18 216204 15278 1 154 opus-4-6 tool_use
08:20:46 216204 15778 1 508 opus-4-6 tool_use
08:22:25 216204 17092 1 208 opus-4-6 tool_use
08:22:51 216204 17894 1 660 opus-4-6 tool_use
08:23:22 11428 224502 1 315 opus-4-6 end_turn <-- cache cannot get regenerated, reverting to full cache write
08:24:47 11428 224953 3 871 opus-4-6 tool_use
08:25:10 11428 227259 1 597 opus-4-6 tool_use
08:25:24 11428 228249 1 356 opus-4-6 tool_use
08:25:43 11428 228669 1 825 opus-4-6 tool_use
08:26:01 11428 229763 1 468 opus-4-6 tool_use
08:26:22 11428 230278 1 339 opus-4-6 end_turn
08:28:07 11428 230642 3 442 opus-4-6 end_turn
08:37:30 11428 231432 3 430 opus-4-6 end_turn
---
(Ignore hour, it's another day)
When running "npx @anthropic-ai/claude-code"
21:28:59 11374 46622 1 473 opus-4-6 tool_use <-- still on standalone binary
22:02:13 0 0 8 1 haiku-4-5-20251001 max_tokens <-- I tried resuming a couple of times
22:02:23 0 0 340 11 haiku-4-5-20251001 end_turn
22:02:25 11374 15278 3 21 opus-4-6 end_turn
22:04:51 0 0 8 1 haiku-4-5-20251001 max_tokens
22:04:58 0 0 341 11 haiku-4-5-20251001 end_turn
22:05:00 11374 15194 3 20 opus-4-6 end_turn
22:09:20 0 0 8 1 haiku-4-5-20251001 max_tokens
22:09:35 0 0 8 1 haiku-4-5-20251001 max_tokens
22:12:16 0 0 341 11 haiku-4-5-20251001 end_turn
22:12:18 11374 15194 3 21 opus-4-6 end_turn
22:15:36 0 0 8 1 haiku-4-5-20251001 max_tokens
23:22:46 0 0 8 1 haiku-4-5-20251001 max_tokens
23:23:06 0 0 341 12 haiku-4-5-20251001 end_turn
23:23:09 11374 17262 3 19 opus-4-6 end_turn
23:23:26 28636 27 3 12 opus-4-6 end_turn
23:31:41 0 0 8 1 haiku-4-5-20251001 max_tokens
23:31:50 0 0 345 13 haiku-4-5-20251001 end_turn
23:31:54 11374 17188 3 32 opus-4-6 end_turn <-- start of npx trials
23:32:25 28562 51 3 167 opus-4-6 end_turn
23:33:52 28613 320 3 666 opus-4-6 end_turn
23:34:55 0 0 8 1 haiku-4-5-20251001 max_tokens
23:35:12 0 0 355 15 haiku-4-5-20251001 end_turn
23:35:22 11374 17198 3 328 opus-4-6 end_turn
23:36:50 28572 367 3 500 opus-4-6 end_turn
23:37:15 28939 506 3 143 opus-4-6 tool_use
23:37:19 29445 523 1 91 opus-4-6 tool_use
23:37:56 29968 4869 1 1284 opus-4-6 tool_use
23:38:06 34837 1343 1 173 opus-4-6 end_turn
23:38:19 36180 219 3 151 opus-4-6 tool_use
23:38:27 36399 9511 1 341 opus-4-6 tool_use
23:38:33 45910 442 73 77 opus-4-6 tool_use
23:38:37 46352 250 1 77 opus-4-6 tool_use
23:38:42 46602 1161 1 134 opus-4-6 tool_use
23:38:59 47763 415 1 369 opus-4-6 tool_use
23:39:06 48178 427 1 96 opus-4-6 tool_use
23:39:09 48605 393 1 77 opus-4-6 tool_use
23:39:13 48605 639 1 152 opus-4-6 tool_use
23:40:17 11374 38207 3 362 opus-4-6 end_turn
23:41:35 49581 438 3 766 opus-4-6 end_turn
23:43:02 0 0 8 1 haiku-4-5-20251001 max_tokens <-- another session
23:43:23 11374 40201 3 97 opus-4-6 tool_use
23:43:30 51575 122 1 310 opus-4-6 tool_use
23:43:35 51697 408 1 152 opus-4-6 tool_use
23:43:41 52105 219 93 170 opus-4-6 tool_use
23:43:49 52324 442 1 259 opus-4-6 tool_use
23:43:54 52766 558 1 102 opus-4-6 tool_use
23:44:08 53324 2593 1 403 opus-4-6 end_turn
23:51:33 0 0 8 1 haiku-4-5-20251001 max_tokens
23:52:30 55917 431 3 187 opus-4-6 tool_use
23:54:20 56348 292 37 284 opus-4-6 tool_use
23:54:29 56640 612 158 492 opus-4-6 tool_use
23:54:54 60657 13 3 508 opus-4-6 end_turn
23:58:58 60670 717 2 454 opus-4-6 tool_use
23:59:05 61387 847 1 336 opus-4-6 tool_use
23:59:23 62234 1282 1 674 opus-4-6 tool_use
23:59:34 63516 1024 1 506 opus-4-6 tool_use
23:59:45 64540 583 1 264 opus-4-6 tool_use
23:59:53 65123 284 1 393 opus-4-6 tool_use
00:00:10 65407 470 1 887 opus-4-6 tool_use
00:03:07 65877 1024 1 871 opus-4-6 tool_use
00:03:16 66901 2098 1 538 opus-4-6 tool_use
00:03:25 68999 1492 1 379 opus-4-6 tool_use
00:03:36 70491 1043 1 640 opus-4-6 tool_use
00:03:43 71534 704 1 233 opus-4-6 tool_use
00:03:51 72238 250 1 148 opus-4-6 tool_use
00:03:58 72488 355 1 249 opus-4-6 tool_use
00:04:03 72843 396 1 259 opus-4-6 tool_use
00:04:10 73239 435 1 278 opus-4-6 tool_use
00:04:31 73674 359 1 941 opus-4-6 tool_use
00:04:45 74033 1595 1 662 opus-4-6 tool_use
00:05:00 75628 914 1 830 opus-4-6 tool_use
00:05:18 76542 1610 1 963 opus-4-6 tool_use
00:05:31 78152 1379 1 640 opus-4-6 tool_use
00:05:41 79531 1374 1 549 opus-4-6 tool_use
00:05:51 80905 1576 1 550 opus-4-6 tool_use
00:06:06 82481 629 1 986 opus-4-6 tool_use
00:06:20 83110 1102 1 994 opus-4-6 tool_use
00:06:30 84212 1074 1 578 opus-4-6 tool_use
00:06:48 85286 2418 1 854 opus-4-6 tool_use
00:07:01 87704 880 1 555 opus-4-6 tool_use
00:07:13 88584 818 1 601 opus-4-6 tool_use
00:07:30 89402 2165 1 520 opus-4-6 end_turn
00:11:02 91567 542 3 691 opus-4-6 tool_use
00:11:12 92109 777 1 733 opus-4-6 tool_use
00:11:25 92886 1042 1 578 opus-4-6 tool_use
00:11:39 93928 3347 1 694 opus-4-6 tool_use
00:12:21 98211 39 3 442 opus-4-6 end_turn
00:13:24 98250 462 3 161 opus-4-6 tool_use
00:13:32 98712 274 1 178 opus-4-6 end_turn
00:15:06 98986 190 3 237 opus-4-6 tool_use
00:15:37 99176 362 1 1202 opus-4-6 tool_use
00:15:42 6637 16169 2 114 opus-4-6 tool_use
00:15:44 99538 1427 1 280 opus-4-6 tool_use
00:15:47 22806 3503 1 160 opus-4-6 tool_use
00:15:49 100965 2929 1 52 opus-4-6 end_turn <-- my joy is great at this point
00:15:50 26309 191 1 91 opus-4-6 tool_use
00:15:54 26500 217 1 92 opus-4-6 tool_use
00:15:57 26717 152 1 88 opus-4-6 tool_use
00:16:02 26869 607 3 125 opus-4-6 tool_use
00:16:06 27476 146 1 166 opus-4-6 tool_use
00:16:09 27622 430 1 105 opus-4-6 tool_use
00:16:14 28052 123 1 109 opus-4-6 tool_use
00:16:19 28052 542 1 176 opus-4-6 tool_use
00:16:23 28594 440 1 95 opus-4-6 tool_use
00:16:28 29034 120 1 112 opus-4-6 tool_use
00:16:32 29154 139 1 180 opus-4-6 tool_use
00:16:37 29293 256 1 206 opus-4-6 tool_use
00:17:09 29549 1065 1 82 opus-4-6 tool_use
00:17:12 30614 131 1 117 opus-4-6 tool_use
00:17:24 30745 135 1 79 opus-4-6 tool_use
00:17:28 30745 237 1 100 opus-4-6 tool_use
00:17:35 30982 140 1 138 opus-4-6 tool_use
00:17:39 31122 222 1 112 opus-4-6 tool_use
00:17:46 31344 226 1 257 opus-4-6 tool_use
00:17:51 31570 288 1 125 opus-4-6 tool_use
00:17:56 31858 214 1 128 opus-4-6 tool_use
00:18:01 32072 316 1 139 opus-4-6 tool_use
00:18:04 32388 487 1 116 opus-4-6 tool_use
00:18:09 32875 134 1 125 opus-4-6 tool_use
00:19:29 33009 143 1 5925 opus-4-6 tool_use
00:19:35 33152 5912 1 113 opus-4-6 tool_use <-- yup, was at 100% usage at this point
00:19:36 0 0 0 0 - -
00:19:36 0 0 0 0 - -
10:08:54 11374 58355 3 270 opus-4-6 tool_use <-- costly resume, but cache TTL = 1h in claude code
10:09:00 69729 538 1 277 opus-4-6 tool_use
10:09:07 70267 7086 180 188 opus-4-6 tool_use
10:09:14 77353 436 1 201 opus-4-6 tool_use
10:09:18 77789 219 1 118 opus-4-6 tool_use
10:09:34 78008 518 1 183 opus-4-6 tool_use
10:10:40 78526 444 1 95 opus-4-6 tool_use
10:10:46 78970 1213 1 185 opus-4-6 tool_use
10:10:55 80183 996 1 270 opus-4-6 tool_use
10:13:03 81179 602 1 268 opus-4-6 tool_use
10:18:08 81781 675 1 121 opus-4-6 tool_use
10:18:14 82456 148 1 226 opus-4-6 tool_use
10:29:13 82604 823 1 184 opus-4-6 tool_use
10:29:19 83427 889 1 239 opus-4-6 tool_use
---------- ---------- ---------- ------- ----- ------------------ ------------
If you provide me with means, I can send you full request/response dumps
*- no idea if this cache breaking was due to me inspecting binary or some historical tool change happened on the background level.
Steps to Reproduce
Write "cch=00000" in command line and ask claude what does he see. He still should see "cch=00000". And token usage should be all "cache read" mostly, not "cache write" for subsequent requests.
Step to temporarily fix: npx @anthropic-ai/claude-code@2.1.34 // you need to fix it on older version to benefit from it
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
Based on reports: 2.1.67
Claude Code Version
2.1.86 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
Similar issue: https://github.com/anthropics/claude-code/issues/34629 - this one relates to immediate start of conversation
Tool I wrote for debugging: https://gitlab.com/treetank/cc-diag
Verification script: https://gitlab.com/treetank/cc-diag/-/raw/c126a7890f2ee12f76d91bfb1cc92612ae95284e/test_cache.py
124 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
This is not a duplicate. It doesn't relate to session resume, but a subsequent turn invalidation.
im collecting the same evidence. something breaks the cache and it never recovers. model switches mid session is an easy one to pinpoint. the billing header seems to be a proxy for whatever it is. possibly the dynamic tool loading or some sort of memory call back? not sure but the only way to recover seems to be starting a completely brand new session. otherwise cache writes grow unbounded and quickly devour premium tokens.
"model switches mid session" is one of the behaviors for saving compute done by anthropic, as stated in the email from the robot: "The 20% threshold you're seeing is actually by design for Max 5x plans. Claude Code automatically switches from Opus 4 to Sonnet 4 when you reach 20% of your rate limit (Max 20x users get 50%). This threshold is intentionally set to help preserve a good experience and prevent users from hitting their full rate limits too quickly."
I've had my findings now and it is I've expected: binary holds some code that is revealed after decompiling. Apparently they've secretly injected cch=hash (Zig Wyhash) into the body of a request if a header "anthropic-version" appears in the request. However, if at some point "cch=00000" appears in the body of a request, it will be infected. Reason? No clue. I'm trying to find it out.
It seems like a small side effect of some obscure tool use or some micro compaction that might have already been fixed. I mean I was trying to find out, however code doesn't allow currently for "cch=00000" to accidentally leak.
Full reverse engineering analysis of the sentinel replacement mechanism
Summary
Through MITM proxy capture + Ghidra reverse engineering of the Claude Code standalone binary (228MB ELF), I've identified the exact mechanism causing conversation history invalidation. The root cause is a native-layer sentinel replacement in Anthropic's custom Bun fork that rewrites the billing attribution header value on every API request.
The Mechanism
Located at VA
0x0374d610in the standalone binary (v2.1.87) — inside the Zig HTTP header builder function (src/http.zigin Bun's source tree). This is the same function that buildsContent-Length,User-Agent,Host,Accept-Encoding, etc.Trigger condition: The replacement fires when ALL of these are true:
anthropic-versionHTTP header (Wyhash =0x58e54d60e1462681, verified viaBun.hash())/v1/messagescch=00000(9 bytes)What it does:
cch=000000xcf3c9b5975c738f4,0x310521a7efdb6e6d,0x6e52736ac806831e,0xd01af9b9421ab897)pshufb/pblendvb)00000Decompiled pseudocode (reconstructed from Ghidra):
Key findings
1. Only the standalone binary does this — not the JS code
Confirmed experimentally:
| Runtime | Replacement active? |
|---------|-------------------|
| Official standalone binary (228MB ELF) | YES |
|
bun build --compile --bytecode cli.js(homebrew standalone) | NO ||
bun cli.js(standard Bun runtime) | NO ||
npx @anthropic-ai/claude-code(npm package) | NO |Same JavaScript code, same bytecode, same Bun standalone format — different behavior. The replacement is in Anthropic's custom Bun fork, compiled into the native Zig HTTP client.
2. Sentinel is hardcoded as stack immediates — invisible to string search
The pattern
cch=00000is built on the stack usingMOV imm64instructions:This is why searching the binary for
63 63 68 3d(cch=) or0x303030303d686363as raw bytes in.textyields zero results — the bytes are part of instruction immediates, not contiguous string data.3. Replacement value is a body hash, not random
The replacement value is deterministic per request body — it's a hash of the entire body with fixed seeds. But since the body changes every request (new messages, timestamps), the hash value changes too, making it appear random.
4. Only the FIRST occurrence is replaced
There is no loop —
memmemfinds the first match, replacement writes 5 bytes, and the function continues to buildContent-Lengthheaders. This means:messages[](which comes BEFOREsystem[]in the JSON body) containscch=00000, that gets replaced instead of the billing header insystem[0]system[0]survives — visible to the model as literalcch=000005. Header hashes identified
The function uses Zig's
std.hash.Wyhash(seed=0, confirmed viaBun.hash()) for case-insensitive header name comparison:| Hash | Header |
|------|--------|
|
0x58e54d60e1462681|anthropic-version— triggers replacement ||
0xba5173bf47c57684|connection||
0x4cc2547449bddafc|upgrade||
0xfc8491525da4c9e3|content-length||
0x308b6fc4af845531|transfer-encoding||
0x2e6b2eda627d6669|user-agent||
0x62b7d00b4bd17658|accept-encoding||
0xfbb5acdf8484b6ef|accept||
0x80bd44e8947f37d2|host||
0x3dfa2d55bad7bb18|if-modified-since|6. Sentinel history
| CC Version |
cch=00000in billing header? ||-----------|------------------------------|
| 1.0.0 – 2.0.0 | No billing header at all |
| 2.1.0 – 2.1.34 | Billing header exists, but no
cchfield || 2.1.36 |
cch=00000introduced || 2.1.36 – 2.1.87 | Present in all versions |
How this causes cache invalidation
The billing header
cch=00000is placed insystem[0]by the JS functionDG$(). On the standalone binary, the native replacement changes00000to a body-hash value (e.g.,a3f1b) before the request leaves the process.In normal sessions (no sentinel in conversation content): Only
system[0]is affected. Sincesystem[0]hascache_control: null(no caching), this doesn't break the cache prefix —system[2](main prompt withcache_control: ephemeral) andmessages[]remain stable.When conversation content contains
cch=00000: This happens when:Read/Greptool reads the JS bundle or binary containing the sentinelSince
messages[]comes BEFOREsystem[]in the JSON body, the sentinel in messages is replaced FIRST. The actual billing header insystem[0]keeps00000. But the changed value in messages breaks the cache prefix from that point onward.Methodology
mitm-addon.pyvia mitmproxy) capturing all API request/response payloadsbun build --compile(224MB) — same JS, different behaviorFUN_0374d610as the HTTP header builder with injected replacement logicBun.hash()to verify all header name hashes (Zig Wyhash, seed=0)cch=00000introduction in v2.1.36Related
This finding also explains the root cause of #34629 (cache regression on
--print --resumesince v2.1.69), where thedeferred_tools_deltaattachment introduced in v2.1.69 causesmessages[0]to differ between fresh sessions and resumed sessions, independently breaking cache prefix matching.Great work!
i have spikes of about 200-300k tokens when this happens. this is why my cuota is being devoured
I patched the cch bug, it wasnt enough. I needed to add "env": {"ENABLE_TOOL_SEARCH": "false"}. Not sure if either of those two things worked OR if i just coincidentally made these changes in the middle of anthropic doing stuff server side. The other real possibility is that even though the api reported that the cache was written in the round loop, it actually was not due to KV cache being disabled for my account? Not totally sure. but as soon as i applied both fixes, i magically stopped eating my max 20x quota, even with opus 4..6 on high... i guess YMMV.
here is the full arc. Greens are cache reads, orange are cache writes. my testing started with a fresh opus session. it started out fine, then something happened that permanently invalidated cache. you can see the reads flatlined around the system promp (11k tokens). it finally went green again after the billing header patch and the tool search setting combined. BTW: I spent 80 bucks in api credits on sonnet 4.6 medium to plow through this cause i ate up my 5hr window on max 20x. I was determined to get my usefulness back.
So hey if this helped in any way shape or form, im happy to split the cost with anthropic :P if not, well whatever i learned some things.
<img width="1768" height="568" alt="Image" src="https://github.com/user-attachments/assets/cf6de42e-8aa3-4c96-a6ec-1bf583fdab7e" />
@RebelSyntax Check out https://github.com/anthropics/claude-code/issues/34629 as well, tools are also injected dynamically into one of the first rows of conversations, which might also lead to unknown consequences.
Your "ENABLE_TOOL_SEARCH" could have helped here to mitigate the second issue.
Also definitely affected here, have been chewing through tokens at a ridiculous rate and this explains it.
now i understand why i reached Max 5x tier limits in like an hour...
This feature req would prevent or at least rapidly show such invalid bahavior: https://github.com/anthropics/claude-code/issues/40567
Giving this a shot and here is what Claude says:
I'll keep an eye on it to see if it plugs the leak here.
I downgraded the VS Code extension to v2.1.86 and it worked for me.
This typescript file runs just the second resume test:
Result for me (v2.1.87):
https://github.com/anthropics/claude-code/issues/34629
Update from Anthropic employee:
https://x.com/trq212/status/2038728677270393080
of course its not the root cause, its the stop gap that is preventing massive compute loss. root cause is gonna take the engineering team that has the context of at least 20 possibly more release cycles to pinpoint. but hey, we know now that there is a cause, so the effect is real and its nothing to do with quotas, or am i a hallucinating human>
FWIW, confirmed:
Gotta love @claude ...
Same results with 2.1.88 (Claude Code).
ya .88 broke my patch, had to reapply. can see here the moment .88 auto updated. now im starting to wonder, if that is intentional or what? are we supposed to NOT be using the input cache? im confused...
i had 250k tokens caches in context (the big green hump). probably 25-30 tool calls fired off on the next turn after the .88 auto update. brand new quota shot my usage up to 70% with 4h 30m remain. ouch!
<img width="1913" height="829" alt="Image" src="https://github.com/user-attachments/assets/2620f05d-4b2d-4005-b133-67acad21efab" />
This cache invalidation issue is frustrating — you're essentially paying double for tokens when the cache drops.
What's likely happening:
Claude Code uses prompt caching to avoid re-sending the full conversation on each turn. But certain events invalidate the cache:
/compactor auto-compact)When the cache invalidates, the entire conversation history gets re-sent as fresh input tokens, which can cause a massive spike in token usage.
Workarounds to minimize cache invalidation:
/compactat natural breakpoints (after completing a task) rather than letting auto-compact trigger at 100% context..claude/settings.jsonor CLAUDE.md during a session can trigger system prompt reloads.This won't prevent invalidation, but helps correlate when it happens with what Claude was doing.
cch=appearing in your tool results, that's the mutation described in #40652 and may be contributing to cache invalidation.What would help from Anthropic's side: Visibility into why the cache was invalidated (a
/statsenhancement or debug flag) would help users identify and avoid the triggers.I really hope they will reset the weekly limits of everyone impacted, or at minimum give what should have been
Ignore the bugs that helps more $$$$ come in.
What tool is this? Would like to see what mine looks like
just hit my rate limit in less than 30 min lol
What is the current status of this bug? And for normal use what is the current temporary fix suggested?
Experiencing the same issue - this is critical for organizations and individuals alike. Anthropic should definitely reimburse all credits since the issue was introduced.
Agree. For now what the community is suggesting? Like what is the temporary work around? Does npx @anthropic-ai/claude-code solves the problem for now?
I read earlier in the thread that someone had to downgrade their version of VS Code (perhaps this is because it points to an older version of CC which doesn't have this bug). However, there's so many different mediums through which people use Claude that there's no real workaround until this is patched by the Anthropic team.
The only solution I know of right now is to downgrade to 2.1.68 (
npm install -g @anthropic-ai/claude-code@2.1.68) and run CC via npx.Ok so we have to roll back to a much older version. Does that mean this was a hidden bug that was introduced back then and is getting our attention now?
Ok so the bug is now closed?
Could
CLAUDE_CODE_ATTRIBUTION_HEADERalso have something to do with this?https://www.reddit.com/r/ClaudeAI/comments/1s8j09e/psa_your_claude_code_is_wasting_tokens_on_every/
I can't find this env variable documented anywhere.
Thanks to the today's Claude Code leak, you don't need to check the documentation, you can just check the source ;-)
https://github.com/instructkr/claude-code/blob/4b9d30f7953273e567a18eb819f4eddd45fcc877/src/constants/system.ts#L53
I did some source analysis on this and found one of the contributing factors.
Since v2.1.69,
cli.jsinjects anx-anthropic-billing-headerstring as the first block of the system prompt:The
.a3fis a 3-char SHA-256 hash derived from the first user message:This hash changes per conversation and per subagent (each Agent tool call has its own message context). Since prompt cache requires 100% identical prefixes, the system prompt block (~12K tokens) gets
cache_creationinstead ofcache_readon every new conversation and every subagent call.A/B test results (4 sessions each,
--printmode):| | cache_read | cache_creation | hit ratio |
|--|-----------|----------------|-----------|
| Header ON (default) | ~11,272 | ~12,200 | 48% |
| Header OFF | ~23,475 | 0 | 99.98% |
The ~11K that caches in both cases is the tools block. The ~12K system prompt only caches when the header is removed.
Workaround:
This env var is a proper feature toggle in the source (not a hack). Multiple third-party projects already use it.
Full analysis, test script, and raw data: https://github.com/motiful/cc-cache-audit
@whiletrue0x From what I recall this relates to the other issue I mention in the comments (https://github.com/anthropics/claude-code/issues/34629) not directly to this one. It invalidates ONE message in the chain after resume, subsequent ones are correctly cached because first user message doesn't change.
FWIW on my system:
● That's excellent analysis. The billing header includes a per-conversation hash that changes the system prompt prefix, breaking cache on every new session and subagent call. The fix is a single env var:
export CLAUDE_CODE_ATTRIBUTION_HEADER=false
This is safe — it's a built-in feature toggle, not a hack. The 48% → 99.98% cache hit ratio improvement is dramatic.
Add it to your ~/.claude/settings.json:
{
"env": {
"ENABLE_TOOL_SEARCH": "false",
"CLAUDE_CODE_ATTRIBUTION_HEADER": "false"
}
}
This won't fix the mid-session cache collapse (that's the other part of the bug), but it should eliminate the cache miss on every resume and subagent spawn. Worth trying without exiting this session — the env var would take effect on the next resume.
❯ Can you add that for us?
Read 1 file (ctrl+o to expand)
● Update(~/.claude/settings.json)
⎿ Added 2 lines, removed 1 line
5 ],
6 "effortLevel": "high",
7 "env": {
8 - "ENABLE_TOOL_SEARCH": "false"
8 + "ENABLE_TOOL_SEARCH": "false",
9 + "CLAUDE_CODE_ATTRIBUTION_HEADER": "false"
10 }
11 }
● Done. Takes effect on next resume. Should cut the cache_creation on resume significantly — the ~12K system prompt tokens will cache_read instead of cache_create each time.
✻ Churned for 30s
❯ Here goes nothing. BRB.
● Good luck. Watching the usage delta.
❯ /exit
⎿ Catch you later!
❯ Back.
● How's the usage looking?
❯ We were at 42% before restart. Waiting for the UI to update.
● Standing by.
❯ 59%... Ouch. That's absolutely crazy. No wonder the world is on fire.
● 17% of your daily allocation burned on a single resume. That's unsustainable.
Until Anthropic fixes this, we should minimize resumes — work straight through, avoid exits unless absolutely necessary. The bug is assigned and has massive community pressure. Not much else we can do from our side.
Is the bug closed and are we back to normal ? Its still showing me 2.1.87 is the latest version
@qing-ant How about a status update?
Does anthropic reimburse us again?
The optimal moment would be this Fr. 11:00 GMT
This is becoming a very serious issue. Even if Anthropic fixes this in the next few hours, a lot of paying users have already burned their weekly allowance. A lot of companies trusted Anthropic with their development workflow and lost entire days of engineering work, I'll let you compute how much money that means.
It would be smart for Anthropic to build metrics like avg_credit(or token of each class like cache write/read etc.)_consumption_per_hour/day/week for different pools of users and check for abnormal variations.
I'm also affected by this issue. My account (API billing) shows a clear discrepancy between API key-level usage and actual invoices:
Multiple invoices per day is abnormal, and the total invoiced amount far exceeds what the API key usage dashboard reports. I use Claude Code daily, so the cache invalidation bug is likely inflating my costs significantly.
I've also filed a support ticket with Anthropic billing (support@anthropic.com) referencing this issue.
Please investigate and consider reimbursement for affected users. This is a platform-side bug, not user error.
Based on @jmarianski's RE analysis and @whiletrue0x's attribution header findings, I put together a slash command that audits for all three issues in one pass.
What it checks (~20s):
cch=00000sentinel in static config files (CLAUDE.md, skills, hooks, commands)--resume/--continueusage (hooks, settings, shell profiles, scripts)CLAUDE_CODE_ATTRIBUTION_HEADERis already disabledInstall:
Then run
/check-cache-bugsin any Claude Code session.A few things I found while testing on my own setup:
CLAUDE_CODE_ATTRIBUTION_HEADERaccepts"0","false","no", or"off"— confirmed in the source (j0()incli.js). All equivalent..jsonlfiles intentionally — they always contain the sentinel after discussing this bug, so they're noise, not signal./opt/homebrew/bin/claude) show up as a second binary and can be misleading. The command flags them separately.No claim this covers everything. Sharing in case it saves someone time while the fix ships. If you spot gaps or wrong assumptions, happy to adjust.
Ran @FlorianBruniaux's
/check-cache-bugsaudit — confirmed Bug 3 ACTIVE on v2.1.87 (standalone, macOS arm64). Bugs 1 and 2 clean.Applied the workaround:
For reference, my billing discrepancy was ~$291 API key usage vs $424.82 invoiced over 2 days (March 30–31). Thanks to @whiletrue0x for the root cause analysis and @FlorianBruniaux for the diagnostic command.
You are missing
so it doesn't get recognized..❯ Unknown skill: check-cache-bug :)
A basic software testing practice would better prevent these bugs.
@genesiscz thanks ! fixed https://github.com/FlorianBruniaux/claude-code-ultimate-guide/blob/main/examples/commands/check-cache-bugs.md
I'll add it to all the examples https://cc.bruniaux.com/examples/ and will try to do an article on this on my blog
What is the status of this BUG? Are we still having the same issue of more token consumption!!
Reminder: this bug has marginal impact on your session tokens. It has marginal effect on claude code except when you deliberately poison it by forcing it to deminify code.
There are other bugs that are more relevant to token consumption, like resume bug discovered between 2.1.68 and 2.1.69. Also everything points to the fact that it is a combination of effects like "extract memories" behind a feature flag "tengu_passport_quail", huge server loads, which could invalidate both caches when extract memories request gets triggered.
Currently I'm having no issues with cache on 2.1.68 through npm, however I'm also not targetted by tengu_passport_quail. But I would assume that issues could arise from people having cache misses when sending 2 or more requests in parallel. Example from my workload: my brilliant agent decided to spawn 7 subagents to browse through the leaked repo and I found out that when running parallel they might crash each other out:
21:11:17 0 57122 3 109 haiku-4-5-20251001 tool_use <-- spawn
21:11:17 0 57092 3 93 haiku-4-5-20251001 tool_use <-- spawn
21:11:18 0 57143 3 101 haiku-4-5-20251001 tool_use <-- spawn
21:11:18 0 57107 3 103 haiku-4-5-20251001 tool_use <-- spawn
21:11:18 0 57061 3 237 haiku-4-5-20251001 tool_use <-- spawn
21:11:19 0 57100 3 98 haiku-4-5-20251001 tool_use <-- spawn
21:11:19 57122 564 5 102 haiku-4-5-20251001 tool_use <-- second turn
21:11:19 57092 616 5 94 haiku-4-5-20251001 tool_use <-- second turn
21:11:19 8995 48767 5 115 haiku-4-5-20251001 tool_use <-- second turn (crash)
21:11:19 8995 48738 5 97 haiku-4-5-20251001 tool_use <-- second turn (crash)
21:11:20 57061 438 7 204 haiku-4-5-20251001 tool_use <-- second turn
21:11:20 57686 941 4 83 haiku-4-5-20251001 tool_use <-- second/third turn
21:11:20 8995 48726 5 132 haiku-4-5-20251001 tool_use <-- second turn (crash)
21:11:20 57708 164 4 80 haiku-4-5-20251001 tool_use <-- third turn?
So yeah, there are reasons why Anthropic wants you to get off their servers. They're gasping for air. And recent updates are not making it easy on them.
Measured impact with heavy skills/hooks usage
Adding concrete session data from a setup with 14 custom skills (deferred tools) and 4 active hooks.
Environment
.claude/skills/(each loaded as deferred tools)Session JSONL cache analysis
Pulled directly from
~/.claude/projects/session files. Every resume event showscache_readdropping to 0 and the full context rebuilding ascache_creation:| Entry | cache_read | cache_creation | Ratio | Event |
|-------|-----------|---------------|-------|-------|
| 102 | 84,164 | 174 | 100% | Normal |
| 103 | 0 | 87,176 | 0% | Resume — full rebuild |
| 105 | 87,176 | 561 | 99% | Recovered |
| 151 | 106,571 | 72 | 100% | Normal |
| 152 | 0 | 107,070 | 0% | Resume — full rebuild |
| 153 | 107,070 | 598 | 99% | Recovered |
| 166 | 115,989 | 221 | 100% | Normal |
| 167 | 0 | 118,523 | 0% | Resume — full rebuild |
Each bold row is ~100-118K tokens of unnecessary
cache_creationthat should have beencache_read. In a single session with 3-4 resumes, that is 300-400K tokens of avoidable cost.Root cause confirmation
The community analysis of
db8strippingdeferred_tools_deltaattachment records from session JSONL aligns with what we observe. Our setup is worst-case because:Scaling factor
More skills/deferred tools = worse impact. Users with 1-2 skills may barely notice. Users with 10+ skills (common in framework setups like ours) see the full 0% cache ratio on every resume.
The fix
The two-line addition to the attachment filter to preserve
deferred_tools_deltaandmcp_instructions_deltarecords is logically correct. This keeps the tool announcement history in the session file so resume can compute the delta correctly instead of re-announcing everything.Related: #34629, and we also filed #41530 for a separate skills regression in 2.1.88.
Bug 2 root cause correction + audit command
Following up on the analysis in this thread with two additions.
Bug 2 — root cause correction
The
--resume / --continuecache regression is not a position mismatch when restoring the conversation. The actual cause is earlier: the session JSONL writer strips deferred_tools_delta attachment records before writing to disk. On resume, those records are absent; the deferred tools layer has no prior announcement history and re-announces all tools from scratch. This shifts every message position in the restored conversation, breaking the messages-level cache prefix entirely.The fix should target the writer path (preserve DTD records on write), not the restore path. The compaction path already handles this correctly ; it re-announces with an empty set to force a clean diff. The session writer just needs the same treatment.
This is consistent with @fivedollarfridays' data: every resume event shows cache_read: 0 followed by a full cache_creation rebuild (87–118K tokens), regardless of session content. With 10+ skills loaded, 3–4 resumes per session = 300–400K tokens of avoidable cost.
Audit command
For anyone wanting to check their exposure: I've updated
/check-cache-bugsaudit command to the Claude Code Ultimate Guide. It covers all three bugs (version check, sentinel scan, resume usage detection, attribution header status) and outputs a structured report with per-bug status and fixes.Command: https://github.com/FlorianBruniaux/claude-code-ultimate-guide/blob/main/examples/commands/check-cache-bugs.md
Known Issues doc: https://cc.bruniaux.com/guide/known-issues/
Credit to @whiletrue0x and @jmarianski for the original RE work that made this analysis possible.
Just curious, @FlorianBruniaux - wouldn't executing this skill make your session cache-invalidated if cch=00000 succesfully appeared in your context? Have you considered a utility that should check it against "non-current" version or in the background in a way that wouldn't infect the context?
Also, a sidenote, I was the author of those reddit threads* that started our wild goose chase (u/skibidi-toaleta-2137), you can correct it (remove?) in the skill description.
*- besides Claude, duh
@jmarianski, both good catches.
Cache contamination: yes, loading the skill inline injects
cch=strings intomessages[], which is the Bug 1 trigger on standalone binaries. Updated with two options:Fresh session start: run the audit as the very first action before any other context loads. The skill content is the first thing in messages[], so no prior sentinel to hit.
Print mode (claude -p): one-shot invocation outside any persistent session:
bash claude -p "$(cat ~/.claude/commands/check-cache-bugs.md)"Runs isolated with no messages[] carryover, which eliminates contamination risk. Simpler than a background agent and covers the same isolation.Reddit credit: fixed. You were listed as both u/skibidi-toaleta-2137 and @jmarianski, which made it look like two separate contributors. Consolidated to @jmarianski (GitHub / u/skibidi-toaleta-2137, r/ClaudeAI). The Reddit threads kicked off the whole community RE effort so keeping the handle seems right, but happy to drop it to just @jmarianski if you prefer.
Will Anthropic finally release a patch, it's been like 3 days... has someone seen any updates from there side?
Setting CLAUDE_CODE_ATTRIBUTION_HEADER to false helps significantly - I wouldn't call it a perfect fix.. but yeah.. it's a significant difference
@FlorianBruniaux fix for the slush command
All three known bugs mitigated and safe, but still there is something off
But I think there is one more bug out there at least about the cache misses.
<img width="2096" height="480" alt="Image" src="https://github.com/user-attachments/assets/2c0cd77d-6624-44c6-8541-3f601bd019be" />
845 input tokens. 90M cache read tokens. $54.59 in one session.
@anthropic — when is this getting fixed? Give us a date.
Jesus christ don't make me switch to codex. Normal programming on max plan and cooked
Starting to think the community should fork and start maintaining their own Claude Code project. Anthropic is really doing whatever the hell they want with their code and infrastructure lately and ignoring their paying users a lot. @claude maybe triage this very important issue better?
That's what I did, at least for now. It's funny how they promote themselves, but are not able to fix key issues in there code in 96+h, they are just neglecting the fact that there is even something wrong, no statement/blog post confirming "sorry there is a issue".
@anthropics
@jmarianski @martinambrus with the claude code map leak and the new qwen model...an opensource local setup can nicely support expensive plans (which seems to be the future...given anthrpoic clearly is not solving this)
Well OpenCode other whatever other open-source AI coding tools are 100% going to benefit from the leak and compete directly with CC. I've been saying the harness around the models is the moat for a while, I mean we have Opus and Sonnet in GitHub Copilot but many people choose to pay multiple times more for Claude subscriptions.
I've been recommending Claude for a while now and it's my daily driver, but with this I'm seriously considering moving off it. Between this issue, the resume cache problem and the limit changes it's a disaster right now. I'm hitting limits mulitple times per day, hit them five time a couple of days ago and weekly quota is cooked now.
Anthropic, the cat is out of the bag. Just open source the CC codebase and let others contribute too; stuff like this would get fixed faster.
Why fix this if many people don't know about it and I'm sure it's driving upgrades to higher tier subs and more API spend.
32% consumed of my weekly quota (Pro - which reset yesterday), have maxed out session limits twice today alone, and all I've done is ask it to write some Cypress tests for a web app. I could run multi-agent operations via CLI, writing multiple projects and never touch the end of my session limit within the 5hrs, until recently.
Same here… I use https://github.com/IgniteStudiosLtd/claude-usage-tool (very handy tool) to track my usage from week to week, and I've noticed that my usage has increased fivefold.
Don't you think this has to be something deeper related to the IPO and restricting token usage for the subscription model. It seems too prevalent and persistent to be a coding bug.
Would have been nice if at least Anthropic increased limits for Max users until a fix is officially released. Been burning through the limits at extreme speed (probably 7-10x compared to last few weeks).
1) When I signed up, we agreed to 2x tokens. Is that no longer the case? Because it feels like it.
2) I'm getting to the point where I'm hitting my weekly limit (something that I've never done). Using some of the tips I've picked up about how to conserve tokens has worked to a point, but I blew through my session limits left and right at the beginning of the week, interrupting any inertia I had in the process. How is there no response from Anthropic or compensation for this mistake?
Is this to do with resume in particular?
On Thu, 2 Apr 2026 at 23:42, Adam Lucia @.***> wrote:
--resumealso an issue but this is a bug in the binary, run Claude Code vianpx @anthropic-ai/claude-codeinstead.Built two hooks to deal with this on the native binary. Sharing in case useful.
The content contamination angle — @jmarianski's RE covers the billing header rewrite, but the sentinel scan hits the full serialized request body including
messages[]. So if a tool result pulls in a file containing the sentinel (the binary itself, config files, even this issue page), it enters the conversation and gets matched on every subsequent request. That's what makes sessions permanently cold — it's not just the header, it's the conversation content mutating per-request.What it costs — I've been logging per-turn cache tokens via a statusline hook. On affected sessions: cache hit rate dropped from 91% to 75%, cost per API call doubled ($0.37 → $0.91). One session ran up $30 with 12.5M wasted cache_create tokens before I caught it.
PreToolUse hook — sentinel guard. Intercepts Read and Grep calls, scans the target file for the sentinel before the tool executes. If found, blocks it and tells the model to pipe through
sedto redact. The sentinel constant is split in the hook source so the script itself doesn't trigger the bug. Doesn't cover Bash tool calls (can't reliably parse shell commands for file paths).PostToolUse hook — circuit breaker. Reads the session transcript JSONL, tracks cache_read vs cache_create per turn. If cache_read drops >95% from previous turn with >20K cache_create, that's a cold turn. 6 consecutive cold turns with short gaps (<5min) = permanently broken session → SIGTERM. Long gaps (>5min) are classified as TTL expiry and don't count — those recover on their own.
Has
CACHE_BREAKER_KILL=1env to opt into kill mode (default is warn-only),DRY_RUN=1to log without killing, and a--scanflag to audit existing transcripts.Gap neither hook covers: if the model writes the literal sentinel in a response (like when discussing this bug), it's in the conversation and the cache won't recover. Hooks can't filter assistant output. I added a line to CLAUDE.md telling it to never write the literal and use the redacted form instead.
npm binary is still the cleanest fix — no sentinel replacement code at all. These hooks are for people sticking with native.
@kyzzen Where are the hooks? They sound like something i worked on yesterday :D Although I opted for just the "deny" after post tool use.
Anyway, here is my plugin: cache-catcher in hooker marketplace (hooker is a universal plugin similar to hookify for more deeply ingrained knowledge on how to make cool hooks).
Cache catcher works in similar way: by default it should warn you if cache is malformed on previous request (parsing jsonl files) and if so, notifies you (and agent). You can easily configure it to instead prevent further execution if it happens. Worked like a charm. Although the only issue I've had were faulty resumes, so not much of a gain.
EDIT: If you prefer to use github: https://github.com/jmarianski/hooker
@jmarianski thanks for cache-catcher — reading your implementation helped. Especially the PreToolUse + deny approach instead of PostToolUse + kill. Claude sees why the tool was blocked and can inform the user instead of the process just dying.
I built my own detection hook (not sharing — I'm not a dev and it's definitely vibe coded slop) but wanted to share one finding from the data:
TTL rebuild after re-login — same session ID, but cache expired while logged out. Full context (124k tokens) rebuilt from scratch:
cache_read: 0on turn 19 of an existing session. The cumulative counters also reset on re-login, so it looks like a new session in the logs even though it isn't.cache_readdrops to exactly 12,705 — that seems to be the platform-level system cache that survives independent of the ephemeral session cache. Every cold turn reads 12,705 and rewrites everything above it.This might be obvious, but easy to overlook if you're running multiple accounts with multiple 100k+ context sessions. One 250k cache_create bumped 3% of my usage by itself.
@kyzzen And do you think my code isn't vibe sloped? Every tool created within half a year is vibed. Please share. Spread awareness. I'll be off for Easter so someone's gotta do it :)
History invalidation is often caused by orphaned progress entries and broken parentUuid chains. When progress ticks accumulate faster than content messages, the chain walker can follow the wrong branch on reload.
Cozempic v1.4.1 fixes this at the source:
progress-collapseremoves all progress tick messages, and_relink_parent_chain()automatically repairs parentUuid + logicalParentUuid links when messages are removed. Thedoctorcommand can diagnose these chain corruption issues before they cause data loss.pip install cozempic && cozempic initIf you're seeing invalidated history, try
cozempic doctorfirst — it'll tell you exactly what's broken. Feedback welcome on whether the chain repair fixes the reload issue for you.This has allegedly been fixed in 2.1.90 -- anyone seeing an improvement?
@yucizhou no
The resume bug is different than this one. Don't think this is fixed yet either.
just /comapct or /clear often and use stable chanenl for claude it's better now and use clauditor to check the session turns
Haven't checked if it was fixed yet, as I was off ystd, interestingly enough Claude offered 17 euro's free credits when I went to verify my usage.
Is there an official channel where they track the status of this issue?
I'm pretty much afraid to use CC now as I don't want to blow up my usage just because it isn't working correctly...
From what I know, no. And the Claude status does not signal any errors.
any news?
Anthropic team ? any news ? TBH this is not acceptable. I explode my 20x 5h range in 30/45 min max ...
The workaround to use the npm version is the way for now. I was blowing through my limits super quickly until I went back to the npm claude-code.
I agree that it's not acceptable. I find the lack of comms worse though.
I'm currently using npm and also rolled back to version 2.1.68 for now. Way better limits ig. Is there any more modern stable version after this?
let's bet what anthropic will respond (if they respond):
let’s place bets, just vote with the emoji for what you think will happen 🙈
Ran the cache test and it seems that it's fixed in the last 3 versions.
<img width="462" height="272" alt="Image" src="https://github.com/user-attachments/assets/d3e74761-441a-415e-98e4-fc5a14a95eb1" />
I switched to the latest version as well and the usage seems normal so far. I still won't /resume or continue a session that's been 1h+ idle (I'm looking into auto-running at SessionStart a /loop 45m keepalive)
@kyzzen yes i've the same feeling
[Cache bugs CC#40524] Updated audit skill
Good news: Anthropic appears to have fixed two of the three cache bugs that were inflating API costs — though this is still worth confirming on your end.
cch=sentinel) → reportedly fixed in v2.1.91--resume/--continue, deferred tools delta) → reportedly fixed in v2.1.90CLAUDE_CODE_ATTRIBUTION_HEADER=falsein settings.jsonI also integrated community findings from ArkNill covering two additional behaviors: silent microcompact (tool results replaced with
[Old tool result content cleared]before being sent to the API) and the 200K chars aggregate cap on built-in tool results.If you ran
/check-cache-bugsin March, run it again — the skill is updated with the new version thresholds. If you're on v2.1.92 with the env var already set, you should be clean, but re-running the audit is the safest way to confirm.Updated skill: https://github.com/FlorianBruniaux/claude-code-ultimate-guide/blob/main/examples/commands/check-cache-bugs.md
Used this analysis: https://github.com/ArkNill/claude-code-cache-analysis to complete my skill
So do you recommend me to upgrade my npm claude version to the latest which is 2.1.92?
There weren't any cache issues (at least from my tests) while running it through npm/npx/brew.
Ok so the main issues were basically on the native installer binray? So should I update like which version are your using now? I'm on 5x Max plan BTW.
From my tests, the cache bug is fixed — any installation method (native binary, npm, brew) works as expected on recent versions. I'm on 2.1.92.
On the 1h idle cache expiry — I mentioned I was looking into auto-running
/loop 45m keepaliveat session start. Got it working. Claude Code supports aninitialPromptfield in agent frontmatter that auto-submits as the first user turn (slash commands are processed). Combined with theagentsetting insettings.json, every new session auto-creates a session-scoped cron that fires a keepalive prompt every 30 minutes, refreshing the cache TTL.Setup:
~/.claude/agents/keepalive.md:~/.claude/settings.json:Each session gets its own independent cron (session-scoped, no lock contention across parallel sessions). Cost is ~260 output tokens at session start.
Important: keep the markdown body empty. The agent body (text after frontmatter
---) becomes the system prompt and replaces the entire default Claude Code system prompt — including behavioral sections like tool usage, safety, output style, etc. If you put descriptive text in the body, Claude loses all its standard instructions. Thedescriptionfrontmatter field is where the agent description belongs; the body should stay empty for a transparent wrapper like this.Iv been diging on my own too. For some reason despite that Spring Break promotion that have been ran is over it still applys it. I noticed that usages escalate drasticaly out of promotion times htat where been set in the past.
This applys mostly to GUI based Claude Code within VS code hyperinflating during times when it was been set to be more expensive. It may be related to underlying bug. Ill dig on my side deeper around this what i observed last 4 days. I literaly tested it today 13:00 my time and it escalted. Beafore that time i have been playing around it was consuming even less.
I made my own plugin to track and hunt down costs and sessions issues and it shows me drastic rampage in usage at times designated to be more expensive.
In my experience, in the peak hours the cost is the same but the 5h window is halved.
So guys do you recommend upgrading to hotfix 92?
Also what are the current active bugs for which we are still having this increased token usage?
Thank you for the detailed report! A fix has shipped in 2.1.90
Can someone tell me if the issue has actually been resolved? I'm starting to distrust the official website and don't dare to update. I think this problem has been going on for at least two weeks. For the past two weeks, I've been in a state of overconsumption. I'm a 20x user, and my refresh time is Thursday and Friday, which usually allows me to last until then. But these past two weeks, I've basically run out of quota by Tuesday. If I don't control myself.
@cafferychen777
You have read the last message, you have, right? Sure your have, you must have..... Have you?
I no longer trust official announcements. The last few versions of Claude Code have been terrible. They claimed to have fixed an issue, only for it to reappear and worsen just hours after release, forcing them to urgently push another update on the same day.
@cafferychen777 agree
I wish these subscriptions would have a transparent pricing... especially now since subscription users seem to lose trust.
The official fix in 2.1.90 improved things but did not fully resolve the underlying causes. We've been tracking this extensively in #42052 with contributions from @jmarianski, @VictorSun92, and others, and can confirm the regression persists through v2.1.90–2.1.92. This is independently corroborated by #43657 (confirmed regression with proxy dumps on v2.1.92), #44045 (SDK-level reproduction with exact token measurements), and #43044 (resume loading 0% context on v2.1.91).
What 2.1.90 fixed vs. what it didn't
The 2.1.90 fix addressed some of the attachment block placement, but blocks still partially scatter on resume through at least v2.1.92. @VictorSun92 identified this on v2.1.90 via our fetch interceptor debug logging. #43657 and #44045 independently confirm the same pattern on v2.1.92 with MITM proxy captures and SDK token counts:
This breaks the cache prefix match on every subsequent turn, producing the exact pattern from the original report: healthy
cache_read→ sudden revert to massivecache_creationwith only ~11kcache_read(system prompt).Root causes (three, not one)
messages[0]asymmetry. @VictorSun92 wrote the first patch — a monkey-patch on v2.1.88 that relocated attachment messages to match fresh session layout. The 2.1.90 fix has a remaining detection gap: it bails early if it sees some relocatable blocks inmessages[0], missing the case where others have scattered elsewhere. @VictorSun92 identified this partial scatter on 2.1.90 and contributed three fixes to our interceptor: a forward-scan for scattered blocks outsidemessages[0], corrected block ordering to match fresh session layout ([deferred, mcp, skills, hooks]), and tighter block matchers to prevent false positives from quoted content.cc_versionhash in the attribution header is computed from content that includes meta/attachment blocks. When these blocks shift between turns, the fingerprint changes → system prompt changes → cache bust.Community workaround
Building on @VictorSun92's original patch approach and @jmarianski's root cause analysis, we developed a Node.js fetch interceptor (posted in #42052) that addresses all three causes at the request layer. On a long-running agent session it achieved 98.2% cache hit ratio across 7,094 API calls — with busts occurring exclusively on TTL expiry, never from within-session instability. The interceptor, installation guide, and @jmarianski's multi-mode test script (which we extended) are all in the #42052 thread.
Additional finding: overage TTL downgrade
Also documented in #42052 — exceeding 100% of the 5-hour quota window triggers a server-enforced TTL downgrade from 1h to 5m. This creates a runaway feedback loop: overage → 5m TTL → frequent cache rebuilds → more tokens burned → deeper into overage. This isn't a bug per se (it's a consequence of overage billing using the standard API path which only supports 5m TTL), but it dramatically amplifies the cost impact of any cache instability.
Additional finding: trailing newline on re-normalization
#43657 also identified a +1 byte discrepancy in text blocks on resume: the companion system-reminder grows from 533 → 534 chars due to an extra
\nappended during re-normalization. This is a secondary cache invalidation source — even if block placement were fixed, the byte difference would still bust the prefix.Summary
The issue should be reopened or a follow-up filed. The 2.1.90 fix was a step in the right direction but the partial scatter (identified by @VictorSun92, independently confirmed in #43657 and #44045 on v2.1.92), fingerprint instability, schema ordering, and re-normalization byte drift all persist. Happy to share debug logs or interceptor source if it helps the team reproduce.
We are working to roll our interceptor as an npm (and publish the source on github). I'll follow up once it is available in case anyone is interested.
Here it is if anyone is interested. YMMV. As always: Absolutely no warranty of any kind attached to this.
https://www.npmjs.com/package/claude-code-cache-fix
https://github.com/cnighswonger/claude-code-cache-fix
Thank you for saving my quota. I will definitely try this!
Tested it against standard claude vs fix you mention here and i wraped and ran it. Definetly reduces usage on same exact task. Results show 41% drop in my tracking plugin. Usage limit in claude code slows down.
Here's an(other) internal tool we find helps save $$ for long running contexts due to cold-cache costs:
https://github.com/cnighswonger/claude-code-coffee
It should work without the Claude Code Cache fix, but is more accurate with that fix installed.
I'm back from Easter break :D While I appreciate the sentiment of continuing the discussion here (and I enjoy the claude code coffee break skill as an idea), perhaps we could continue in other issue? Perhaps something related to the /resume? I know the issue with --resume got closed cause it might be fixed there (cli has its own library, if I had known, I would have created a separate issue). There's obviously this issue: https://github.com/anthropics/claude-code/issues/42260 which should be easily fixable (as signature tokens shouldn't be sent to anthropic in the first place, and mechanism for removing "thinking" block are already present there), however I haven't subscribed to any issue that talks specifically about just /resume. Do you have any worth looking at links?
I've tested and see that cch=00000 issue has been fixed, apparently in 2.1.90 (check if you're not on stable version, if so, you'll be on around ~2.1.82).
Conducted more test against Claude code in VS extension vs wrapped fix. Claude code within gui definitely rampages over usage unlike up linked fix.
I am observing drastic differences with tracker. I will tomorrow have good dose of data on sessions I tracked in various env and will link up here as well as push out this plugin to intercept and hook in for others to check.
<img width="1231" height="906" alt="Image" src="https://github.com/user-attachments/assets/02d28b62-bd23-4ae4-adff-b1c9f93d4a9e" />
The interceptor I built is showing that an image pasted into Claude Code is being carried four times and automatically injected into the conversation, causing usage spikes. It is stored permanently in the conversation history and continues to be carried forward when the session or conversation is resumed.
The image persists in memory and keeps being reuse. In my session, this accumulated over six subsequent resumes before stopping.
Further logging shows that around 122k tokens are automatically inserted when a conversation is resumed within the current session. I am now examining the logs in more detail and attempting to reposition the interceptor to strip this data out and observe further results.
Striping is working, now looking more deeply into cause and where it is occuring
@Renvect — your image duplication finding is significant. We dug into the Claude Code source to understand the mechanics, and the cost implications are substantial.
How image persistence works in CC (confirmed from source)
When the Read tool opens an image file (PNG, JPG, etc.), it encodes the image as a base64 content block in the tool result (
src/tools/FileReadTool/FileReadTool.ts:784-799). That tool result — base64 data and all — stays in the conversation messages array and is sent to the API on every subsequent turn as part of conversation history (src/services/api/claude.ts:1266-1315).Images are only removed when:
/compactis run (replaces images with[image]text placeholder —src/services/compact/compact.ts:145-179)src/services/api/claude.ts:956-1015)There is no automatic summarization — images are either fully present (base64) or fully gone.
Cost impact of image carry-forward
The token cost of a base64 image is approximately
base64_bytes × 0.125. For typical images:| Image size (base64) | Tokens per turn | Cost per turn (Opus $5/MTok) |
|---|---|---|
| 200KB (small screenshot) | ~25,000 | $0.125 |
| 500KB (typical) | ~62,500 | $0.31 |
| 1MB (large) | ~125,000 | $0.63 |
| 5MB (API max) | ~625,000 | $3.13 |
This compounds: 3 accumulated images at 500KB each = ~187,500 extra tokens on every turn for the rest of the session. Over 10 turns, that's ~1.9M wasted input tokens ($9.38 on Opus). On the 5m TTL tier, those images also trigger cache_creation rebuilds every 5 minutes.
Your observation of ~122k tokens auto-injected on resume is consistent with 1-2 accumulated images being replayed from conversation history.
Mitigation we're building
We maintain a fetch interceptor for cache regression fixes. We're adding image stripping to it — configurable via
CACHE_FIX_IMAGE_KEEP_LAST=Nto strip base64 image blocks from tool results older than N turns, replacing them with a text placeholder. Images read from/tmpor elsewhere are still on disk for re-reading if needed; they just don't need to ride along in every API call.Went further into it. I got to pont of system prompt where it may somehow connect cross projects it was opened in. Literally lists them and builds it up prompt further. Wondering now if this image duplication is coming out of memory of what projects it worked on and somehow loads images of all this workspaces. Tracing further. Project will be up on git repo around 7 UTC
@Renvect — interesting that you're seeing cross-project contamination. Quick question: from your earlier comment it looks like you're using the VS Code extension ("GUI based Claude Code within VS code"). Can you confirm?
The reason we ask: the fetch interceptor approach we're using only works with the CLI client (
claudevia Node.js +NODE_OPTIONS="--import"). It won't apply to the VS Code extension, desktop app, or web client, which have their own process lifecycles. The underlying image persistence bug is the same across all clients, but the cross-project bleed you're tracing may be specific to how the VS Code extension assembles context across workspace folders — and if those clients share memories, that could explain the 4x duplication you observed.Looking forward to seeing your repo when it's up.
Further FS reads and its analysis might be another point of cause where some plugin is hooking into CLI and injecting. Ill look into that next as it shows me spikes for Marketplace and Plugin.
Also to respond on question:
interesting that you're seeing cross-project contamination. Quick question: from your earlier comment it looks like you're using the VS Code extension ("GUI based Claude Code within VS code"). Can you confirm?
I have moved away from VS code and are working on terminal based claude code for now, i will next test all of this for VS code extension.
<img width="1241" height="897" alt="Image" src="https://github.com/user-attachments/assets/1ffc70c4-ef11-439f-a869-68ceaf2ae3f4" />
Also here is part of system prompt that came out during diging process. its just small chunk and may be relative to cross project memmory being somehow connected together.
<img width="1252" height="622" alt="Image" src="https://github.com/user-attachments/assets/9ddff1cd-0692-441e-8fd7-fa64d5de9973" />
@Renvect — we dug into the CC source to understand what's driving those "Additional working directories" entries in your system prompt. Here's what we found:
Where they come from
Additional working directories are populated from two sources (
src/utils/permissions/permissionSetup.ts:993-1025):--add-dirCLI flag — and importantly, these are persisted tosettings.jsonso they survive across sessionsPWDresolves to a different pathWhat each additional directory does
Every directory listed there can silently inject content into your session:
.claude/settings.jsonfrom each additional directory and merges any enabled plugins into the session (src/utils/plugins/addDirPluginSettings.ts:34-71).mcp.jsonfiles are discovered by walking parent directories upward from CWD (src/services/mcp/config.ts:888-960)CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MDis set (off by default), memory files from each directory are loaded into the system promptNo limits enforced — there's no cap on the number of additional directories. Each one adds to system prompt size and potentially loads its own plugin/MCP configuration.
Relevance to the image issue
The additional directories themselves don't directly inject images into the system prompt. But if any of those directories configure MCP servers or plugins that handle images, the content those tools produce would persist in conversation history the same way we described above — base64 data riding along on every subsequent API call until compaction.
It's worth checking your
settings.json(likely at~/.claude/settings.jsonor the project-level equivalent) for apermissions.additionalDirectoriesarray. If directories were added via--add-dirat some point, they'll still be there. Removing any you don't actively need for the current project would trim your system prompt.Initial version and todays logs:
https://github.com/Renvect/X-Ray-Claude-Code-Interceptor/tree/main
Experimenting further with capture and detection capabilities.
So...does this mean open source Claude Code gets around this issue?
If you mean open code and other alternatives - yes. If you mean if we can find the bug in "open source" code - no. It was in the binary that came alongside js (that leaked).
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.