[BUG] Prompt Cache: Full Conversation Prefix Re-written 3 times Seconds Apart (Multiple Sessions & Models)
Prompt cache: full conversation prefix re-written on turns seconds apart — 31% of 3 weeks of cache-write spend, recurring across 4 model families
Summary
Claude Code intermittently re-writes the entire conversation prefix to the prompt cache on a
turn that should have been a near-total cache hit. cache_read_input_tokens collapses to just
the static system-prompt + tools segment (~20–24k) while cache_creation_input_tokens jumps to
the full context size (observed up to 899,075 tokens in a single request).
I scanned 3 weeks of my own local transcripts: 732 sessions, 13,688 API requests, 83,666,423
total cache_creation_input_tokens.
- 85 full-prefix rewrite events across 27 sessions and 5 projects
- Those 85 events = 26,211,061 cache-write tokens = 31.3% of ALL cache-write in 3 weeks
- 25 of them happened with less than 300s of idle time before the request — inside even the
shortest cache TTL. That subset alone is 7,454,315 cache-write tokens ≈ 9.3M
input-token-equivalents (writes bill at 1.25×)
- 11 events with under 60s idle; the tightest is 11 seconds
- 9 events read back 0 tokens — even the static system prefix missed
- Occurs on **
claude-opus-4-8(30),claude-opus-5(42),claude-fable-5(11),
claude-sonnet-5 (2)** — not model-specific
- Also occurs in subagent and workflow transcripts, not just main sessions
The user-visible symptom is a session budget vanishing with nothing to show for it. In the
incident that prompted this report, 743,839 cache-write tokens were billed in 70 seconds while
total output for the window was 17,334 tokens — roughly 30% of a 5-hour session limit consumed
by cache writes, not generation.
I am reporting two things: (A) the rewrite-on-consecutive-turns behaviour, and (B) a
session-usage accounting question at the limit-window reset boundary.
Environment
- Claude Code CLI, macOS (Darwin 25.5.0), zsh
- Models affected:
claude-opus-4-8,claude-opus-5,claude-opus-5[1m],claude-fable-5,
claude-sonnet-5
- Contexts involved: 100k–900k tokens
- Hooks active in the affected sessions: a
UserPromptSubmithook (per-prompt context
injection), a PreCompact hook, SessionStart hook output
- Heavy Edit/Bash tool loops; some sessions include image attachments and subagent/workflow runs
- The primary incident session declared: *"This session's requests use a 1-hour Anthropic
prompt-cache TTL", with the caveat "If the session enters usage overage, later requests drop
to the 5-minute TTL"*. The account was in usage overage during that incident.
Times below are Europe/Berlin (CEST, UTC+2).
(A) Full-prefix rewrite on a turn seconds after the previous one
Clearest single repro — two rewrites 41 seconds apart
Same session, claude-fable-5, consecutive requests:
13:35:14 cacheWrite=370497 cacheRead=24119 req_011CdYAso2C4HxMbkLM95ZgW
13:35:55 cacheWrite=373342 cacheRead=24119 req_011CdYAxUfH9AjBEotAUfKxw <- 41s later, missed again
13:36:24 cacheWrite= 1804 cacheRead=397461 req_011CdYB1Fycqjboh1pgUStaQ <- recovered
The 370,497 tokens written at 13:35:14 were billed and never read by any request. The turn
41 seconds later re-wrote the same prefix from scratch. The turn after that hit correctly,
reading 397,461. No TTL explains a miss at 41s, and the recovery one turn later shows the data
was cacheable the whole time.
4 of the 85 events are immediately followed by another full rewrite like this.
Cross-session evidence: rewrites with <300s idle before them
Every one of these is inside even the 5-minute overage TTL, so eviction cannot be the cause:
date/time (Berlin) model idle cacheWrite cacheRead
2026-07-28 10:20 claude-opus-5 82s 899075 20601
2026-07-23 17:22 claude-opus-4-8 245s 411677 20532
2026-07-27 17:47 claude-opus-5 70s 398768 20601
2026-07-24 18:49 claude-fable-5 17s 389753 24305
2026-07-28 11:17 claude-opus-5 50s 378091 20601
2026-07-30 13:35 claude-fable-5 41s 373342 24119
2026-07-24 18:39 claude-fable-5 14s 359981 24305
2026-07-24 21:05 claude-opus-5 208s 346415 24105
2026-07-30 12:37 claude-fable-5 99s 340873 24119
2026-07-27 16:49 claude-opus-5 82s 318111 20601
2026-07-25 07:48 claude-opus-5 48s 300892 21997
2026-07-30 12:13 claude-fable-5 38s 299100 24119
2026-07-16 17:46 claude-opus-4-8 152s 253730 23401
2026-07-27 13:29 claude-sonnet-5 271s 247018 28923
2026-07-28 17:31 claude-opus-5 179s 232208 24109
2026-07-28 17:25 claude-opus-5 41s 219377 24109
2026-07-24 17:42 claude-fable-5 52s 211015 24305
2026-07-17 18:14 claude-opus-4-8 11s 208908 56930
2026-07-28 14:34 claude-opus-5 104s 203921 24109
2026-07-30 11:37 claude-fable-5 158s 199215 24119
2026-07-13 11:24 claude-opus-4-8 82s 187750 21749
2026-07-13 12:01 claude-opus-4-8 23s 185656 0 (workflow subagent)
2026-07-16 17:58 claude-opus-4-8 104s 181065 23722
2026-07-28 17:07 claude-opus-5 49s 120624 0 (subagent)
(One row appears twice in my scan because a resumed session logs the same requestId in two
transcript files — 24 distinct events, 25 records.)
The escalation pattern
Within a single session, each successive rewrite costs more, because the prefix being re-written
keeps growing. One session, 558 requests:
2026-07-27 16:30 cacheWrite=240995 cacheRead=20601
2026-07-27 16:49 cacheWrite=318111 cacheRead=20601
2026-07-27 17:47 cacheWrite=398768 cacheRead=20601
2026-07-27 21:15 cacheWrite=562592 cacheRead=20601
2026-07-27 22:13 cacheWrite=677381 cacheRead=24108
2026-07-28 08:00 cacheWrite=740887 cacheRead=20601
2026-07-28 09:54 cacheWrite=784944 cacheRead=20601
2026-07-28 10:20 cacheWrite=899075 cacheRead=20601 <- 82s idle before this one
8 events = 4,622,753 tokens = 83% of that session's entire cache-write spend. The last one
re-wrote 899k tokens after 82 seconds of idle.
cache_read_input_tokens is pinned at exactly the same value (20601 / 24119 / 24305 / 21997,
depending on session) across every rewrite in a session, over many hours. Only the first cache
breakpoint — static system prompt + tool schemas — ever hits on these turns. The
conversation-body breakpoint is written and never read.
Expected vs actual
Expected: a request issued seconds after the previous one, same model, same session, prefix
unchanged apart from appended turns, reports cache_read_input_tokens ≈ N andcache_creation_input_tokens ≈ delta.
Actual: cache_read_input_tokens drops to the static-prefix size and the full body is
re-written. Sometimes twice in a row before it recovers on its own.
Idle-gap distribution for all 85 events
<60s 11 (13%)
60-300s 14 (17%)
300-3600s 21 (25%)
>3600s 37 (45%)
median 1102s
The 37 events after >1h idle are legitimate expiry and I am not claiming those are bugs — but
see the visibility request below, because a legitimate 900k-token rewrite is still a large
silent charge. The 25 events under 300s are the defect.
(B) Session-usage accounting at the limit-window reset
Timeline for the incident that prompted this, on 2026-07-30:
| time (Berlin) | event |
|---|---|
| ~13:23 | hit 100% of the 5-hour session limit; continued on usage credits (overage) |
| ~13:29 | Fable 5 (effort: high) actively working a task |
| 13:30 | session limit window resets |
| 13:31 | /usage still reported 100% session usage, one minute after the reset |
| ~13:33 | /usage reported ~30% of the new window already consumed |
| 13:35:14 | full prefix rewrite, 370,497 tokens |
| 13:35:55 | full prefix rewrite, 373,342 tokens |
| 13:36:31 | user interrupts all work |
| 13:36:45 | /compact |
| 13:40:45 | switch to Opus 5 to investigate |
Transcript-verified: the interrupt, compact, and model switch match to the second.
Two observations:
- At 13:31, one minute after the reset,
/usagestill showed 100%. The displayed window
boundary and the billing window boundary may not be on the same clock.
- The ~13:33 reading of ~30% consumed predates any billed request in the new window. The
transcript shows no API request at all between 13:27:15 and 13:35:14. Either pre-reset
spend was attributed to the post-reset window, or my ~13:33 recollection is off by ~2 minutes
and the reading actually came after 13:35:55 — in which case (A) fully accounts for it and (B)
is not a bug. I cannot settle this from the transcript; flagging it in case the reset-boundary
accounting is worth a look on your side.
Requests
- Fix the sub-300s rewrites. A request seconds after a successful cache write should not
re-write the whole prefix. The self-recovery one turn later suggests a write-visibility or
breakpoint-identity problem rather than expiry.
- Verify the declared TTL is actually applied to the conversation-body breakpoint, not only
the system prefix. Sessions advertising a 1-hour TTL show body misses after 8 minutes.
- Surface
cache_creation_input_tokensspikes in/usageor the status line. Today a
370k–899k prefix rewrite is completely invisible; it looks identical to normal work while
consuming a third of a session budget. This is the single change that would have saved me from
burning two session windows without knowing why.
- Check cache-breakpoint stability when hooks inject content mid-conversation. In the
13:35 incident, hook output landed at 13:35:31 and 13:35:41 — between the two failing
requests. A UserPromptSubmit hook appending per-prompt text, or file-state
system-reminders, could be shifting content behind a breakpoint and invalidating everything
after it.
- Reconsider shortening the cache TTL on overage (or warn clearly). Overage is exactly when
a miss is most expensive, and the shortened TTL makes misses more frequent — a feedback loop
that accelerates spend precisely when the user is already paying premium.
- Consider crediting back demonstrable sub-TTL rewrites. 9.3M input-token-equivalents over
three weeks from the <300s subset alone is not a rounding error.
Note for anyone reproducing this analysis
Transcripts emit one assistant record per content block, each carrying a copy of the sameusage object. My primary session logged 471 assistant records for 220 real requests. Dedupe byrequestId or you will over-count by ~2×.
Detection predicate used: cache_creation_input_tokens > 100_000 AND cache_read_input_tokens <, deduped by
0.3 * cache_creation_input_tokensrequestId, with idle gap measured to the
previous request in the same transcript.
Local transcripts with full per-record usage (468 MB across 732 sessions) are retained and can
be supplied on request, including the two requestIds named above.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗