`--continue`/`--resume` cache invalidation still reproduces on v2.1.116 — TTL-matched control shows 41–99pp hit-rate delta
Summary
Issue #42338 was closed after v2.1.90/91/92 attempted to fix session-resume prompt-cache invalidation. I can reproduce the same behavior on v2.1.116, 24 versions later. This report contributes a new methodology: TTL-matched control pairs that isolate the resume event itself as the cause, independent of cache TTL expiry.
Version
$ claude --version
2.1.116 (Claude Code)
Why a new issue (not #42338)
The bot auto-locked #42338 on 2026-04-11 after 7 days of inactivity with the standard "please file a new issue" message. Filing here per that instruction.
Methodology — TTL-matched control pairs
Prior reports in #42338 measured single-case token counts (e.g. "2-12s exit→resume produces 470–512k cache_creation"). A skeptic could still argue "maybe the 5-minute TTL happened to tick over." To fully eliminate TTL as a confound, I pulled per-turn usage from 14 jsonl session files for one long-running Claude Code agent on the same machine, deduplicated by requestId, and built two comparison sets:
- Baseline: consecutive turns inside the same jsonl separated by idle gaps of 3–28 min.
- Test: transitions where one jsonl ends and a new jsonl begins (i.e. a resume / new process) with the same 3–28 min idle gap.
Any cache-hit delta between the two sets at matched gap duration cannot be explained by TTL, because the gap is identical. The delta is therefore attributable to whatever differs between the two cases — the resume event itself (new process, rollout replay, deferred-tools/attachment reconstruction).
Observation 1 — Claude Code uses the 1-hour cache internally
All cache_creation.ephemeral_5m_input_tokens are 0; all cache_creation flows into ephemeral_1h_input_tokens. So within-session idle up to ~60 min should stay warm.
Observation 2 — Baseline (mid-session, same jsonl)
| gap | cache_creation | cache_read | hit % |
|---:|---:|---:|---:|
| 3.3m | 389 | 155,492 | 99.8% |
| 3.3m | 376 | 80,990 | 99.5% |
| 3.7m | 2,123 | 159,700 | 98.7% |
| 4.0m | 3,209 | 107,456 | 97.1% |
| 4.0m | 5,112 | 173,721 | 97.1% |
| 4.3m | 2,015 | 220,492 | 99.1% |
| 4.8m | 840 | 78,457 | 98.9% |
| 5.7m | 264 | 82,612 | 99.7% |
| 5.8m | 3,302 | 85,549 | 96.3% |
| 5.9m | 1,283 | 240,818 | 99.5% |
| 6.0m | 2,385 | 161,823 | 98.5% |
| 7.6m | 4,945 | 114,763 | 95.9% |
| 27.4m | 1,559 | 150,593 | 99.0% |
Average ≈ 98% cache hit. The 1-hour cache works correctly when the session process stays alive.
Observation 3 — Cross-session transitions (same agent, new jsonl file)
| transition | gap | next cc | next cr | hit % |
|---|---:|---:|---:|---:|
| f3f5c819 → e17536e7 | 28.2 min | 40,260 | 0 | 0.0% |
| e17536e7 → 096d96fa | 4.1 min | 23,849 | 16,702 | 41.2% |
| c0eb34ad → b6e07fac | 2423.6 min (>1h TTL) | 20,061 | 0 | 0.0% (expected) |
| b6e07fac → f3f5c819 | 2059.3 min (>1h TTL) | 41,766 | 0 | 0.0% (expected) |
Observation 4 — Direct TTL-matched pairs
| gap (both rows) | same-session (baseline) | cross-session | delta |
|---:|---:|---:|---:|
| ~4 min | 97–99% hit (cr > 100k, cc ≈ 3k) | 41% hit (cr 16.7k, cc 23.8k) | −56 pp |
| ~28 min | 99% hit (cr 150k, cc 1.5k) | 0% hit (cr 0, cc 40.3k) | −99 pp |
Same idle duration, same process workload, same codebase state. The only variable is "session boundary crossed vs not." The delta isolates the resume event itself as the cache-breaker.
Alignment with the v2.1.69 deferred_tools_delta root cause
The 4.1-min case shows partial prefix match (16,702 tokens of cr + 23,849 tokens of cc) rather than a clean 0/full miss. This pattern — "prefix matches until block N, then a reshuffle breaks it" — is consistent with the attachment-reordering / tool-result shuffling hypothesis from #42338. A pure TTL miss would show 0% hit with no partial matching at all.
The 28.2-min case shows 0% — consistent with a reshuffle occurring earlier in the prefix (e.g. skill_listing, todo_reminders, or nested_memory injection at a different position, as simpolism catalogued in https://gist.github.com/simpolism/302621e661f462f3e78684d96bf307ba).
Quota impact (one 11h 52m session, 197 unique turns)
| metric | value |
|---|---:|
| cache_read total | 60,869,087 |
| cache_creation total | 2,017,110 (all 1-hour, 2× write premium) |
| input_tokens total | 9,923 |
| output_tokens total | 446,687 |
| cc / output ratio | 4.52× |
For context, well-cached sessions typically run at 1–2× cc/output. The 4.5× on this session was dominated by a single cross-session 28.2-min transition (40k cc that a TTL-preserving resume would have saved) plus two honest >1h TTL misses.
For subscription-plan users, this directly consumes the rolling 5-hour quota. On this agent's overnight watchdog-driven restarts, the bug caused ~20 % of a 5-hour quota to be burned on resume-induced cc alone.
Reproduction
- Start a Claude Code session, accumulate ≥ 100k tokens of context.
- Exit (
/exit) and immediately restart withclaude --continuewithin 1–5 min. - On the first turn of the resumed session, check
usage.cache_read_input_tokensvscache_creation_input_tokensin your monitoring proxy or in the jsonl under~/.claude/projects/<slug>/<sid>.jsonl. - Compare to a control: take a turn inside the original session after the same idle gap (no exit). Baseline is ≥ 95%
cache_read.
Environment
- macOS 15.x (Darwin 25.4.0), Apple Silicon (M4)
- Claude Code 2.1.116
- Subscription plan
- Active features during affected sessions: custom agents, MCP servers (fetch, sequential-thinking, filesystem, memory, discord plugin, computer-use, gmail, google-calendar), hooks (SessionStart, SessionEnd, PreCompact, PostToolUse), skills, CLAUDE.md
Asks
- Reopen or fold this into an active tracking issue so the status is discoverable.
- Confirm whether the known attachment-reordering fixes (v2.1.90/91 et al.) are expected to cover custom-agent + MCP + skill combinations, or if there are known unfixed paths.
- If a partial/staged fix is planned, consider exposing a
--no-deferred-tools-deltaor--strict-prefixopt-out flag so harness users can trade feature completeness for cache stability.
References
- #42338 (closed + locked) — original report
- #34629 — related
--print --resumeregression since v2.1.69 - #46829 — silent TTL default 1h → 5m regression
- https://github.com/ArkNill/claude-code-cache-analysis — ArkNill's broader cache analysis
- https://gist.github.com/simpolism/302621e661f462f3e78684d96bf307ba — simpolism's patch identifying the breaking attachment set
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗