anthropics / anthropics/claude-code

[BUG] Prompt cache for resumed session history intermittently fails to hit on `resume`, independent of idle time or TTL

Aperta
#92,938 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area:agent-sdk area:core area:cost duplicate platform:windows
Lingua principale
Python
Stelle
145k
Fork
23.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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?

# Bug Report: Claude Agent SDK — Prompt cache for resumed session history intermittently fails to hit on `resume`, independent of idle time or TTL

## Summary

When using the Claude Agent SDK's `resume` option (equivalent to CLI `--resume `) to continue a session across separate `query()` invocations, the cached conversation-history portion of the prompt intermittently fails to hit cache and gets fully rewritten as a new `cache_creation` — even when the resume happens seconds after the prior turn ended, well inside the prompt-cache TTL, and even when a separate resume of the *same session minutes or tens of minutes later* hits cache successfully. The static system-prompt/tool-schema portion of the same request consistently hits cache in every case, including the cases where the conversation-history portion misses — indicating the failure is isolated to the cache breakpoint covering prior turns, not the request as a whole.

## Environment

- Claude Agent SDK (Node/TypeScript), package `@anthropic-ai/claude-agent-sdk-win32-x64`, version `0.3.261`
- OS: Windows 11
- Model: `claude-fable-5-1[1m]` (1M-context variant)
- Account has 1-hour extended prompt caching enabled
- Session resumed via the SDK's `resume: ` option on each new `query()` call, with only the new incremental user message sent as prompt input — full conversation history is expected to be reconstructed by the CLI from the on-disk session file under `~/.claude/projects/.../.jsonl`

## Actual behavior

The same "resume shortly after last turn" scenario intermittently and unpredictably falls back to writing the entire conversation history as a fresh `cache_creation`, at roughly 10x the price of a cache read, with no discernible trigger tied to elapsed time, message content, or turn structure.

### Reproduction data (same session, times UTC)

| Resume time | Elapsed since prior turn ended | Result |
|---|---|---|
| 18:05:53 | 55 min | cache **hit** (98%) |
| 18:09:37 | 3.4 min | cache **miss** (6%) |
| 18:13:45 | 3.0 min | cache **hit** (97%) |
| 18:27:15 | 12.4 min | cache **miss** (5%) |
| 18:42:47 | ~0 (immediate) | cache **hit** (99%) |
| 18:43:23 | ~0 (immediate) | cache **hit** (100%) |
| 19:06:32 | 23 min | cache **miss** (6%) |

Key observations from this data:

- **Elapsed time does not predict the outcome.** A 55-minute-old resume hit cache cleanly, while a 3.4-minute-old resume immediately after it missed. This rules out any simple TTL-expiry explanation (extended 1-hour caching was active throughout).
- **On every miss, `cache_read_input_tokens` collapses to the same small fixed value (~6,459 tokens)** — consistent with only the static system-prompt/tool-schema portion of the request surviving, while the entire conversation-history portion (in one case >170,000 tokens) is rewritten from scratch.
- **This happens on the single most common turn-completion path** — an ordinary next-user-turn resume after Claude's previous reply fully completed, not any unusual state (no aborted queries, no error recovery, no multi-session merge involved in these events).
- Four additional independent reset events were captured in a separate long-running session with the same signature: full cache rewrite of the historical portion on a `resume` call, with gaps between the prior turn and the resume of 6.14, 20.59, 6.29, and 3.67 minutes respectively — again showing no consistent relationship to elapsed time.

## Additional notes

- The intermittency (same code path, same session, minutes apart, alternating hit/miss) suggests this is not deterministically tied to any specific message content or turn shape — I compared the JSON of the assistant messages immediately preceding several hit and several miss events (including plain single-block text replies and multi-step tool-use turns) and found no consistent structural difference between the two groups.
- This may be related to interaction between prompt caching and the 1M-context (`[1m]`) beta variant specifically, since all captured examples used a 1M-context model; it has not been verified against the same conversation on a standard 200K-context model.
- Financial impact is significant for any long-running session that relies on `resume` across many short-lived `query()` calls, since each unpredictable miss re-bills the entire accumulated conversation history at full cache-write price instead of the ~10x cheaper cache-read price.

## Ask

Please investigate whether the resume/`--resume` code path has a known race or eviction condition affecting the conversation-history cache breakpoint specifically (as opposed to the system-prompt/tool-schema breakpoint, which was never observed to fail). If this is already addressed in a later CLI release, confirming the fix version would be appreciated so we can validate against these reproduction steps.

### What Should Happen?

## Expected behavior

A `resume` of an existing session, made shortly after the prior turn ended, should hit the previously cached conversation-history prefix (`cache_read_input_tokens` close to the full prior context size, `cache_creation_input_tokens` covering only the newly added turn) — consistent with what already happens for the *majority* of resumes in the same session.

### Error Messages/Logs

```shell
No error is thrown — the request completes successfully with stop_reason: end_turn. The only observable symptom is in the response's usage block, where cache_read_input_tokens drops to a small fixed value and cache_creation_input_tokens covers the full prior conversation instead of just the new turn.

### Reproduction data (same session, times UTC)

| Resume time | Elapsed since prior turn ended | Result |
|---|---|---|
| 18:05:53 | 55 min | cache **hit** (98%) |
| 18:09:37 | 3.4 min | cache **miss** (6%) |
| 18:13:45 | 3.0 min | cache **hit** (97%) |
| 18:27:15 | 12.4 min | cache **miss** (5%) |
| 18:42:47 | ~0 (immediate) | cache **hit** (99%) |
| 18:43:23 | ~0 (immediate) | cache **hit** (100%) |
| 19:06:32 | 23 min | cache **miss** (6%) |
```

### Steps to Reproduce

## Steps to reproduce

1. Start a session and drive several turns of normal conversation (mix of plain text replies and tool-use turns), so a large prompt-cache checkpoint builds up over the conversation history.
2. Let the query/process for a turn close normally (turn ends with `stop_reason: end_turn`, no pending tool calls).
3. Start a new `query()` call with `resume: `, sending only the next user message as prompt input (not replaying history — relying on `--resume` to load it).
4. Inspect the `usage` block of the resulting response: `cache_read_input_tokens` vs `cache_creation_input_tokens`.
5. Repeat step 2–4 across many turns in the same long-running session and log the result of each resume.

### Claude Model

Not sure / Multiple models

### Is this a regression?

I don't know

### Last Working Version

_No response_

### Claude Code Version

2.1.265 (@ time of submission)

### Platform

Anthropic API

### Operating System

Windows

### Terminal/Shell

Other

### Additional Information

_No response_

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

Start with the Claude Agent SDK query() resume option and the CLI --resume path, using the session file under ~/.claude/projects/.../.jsonl. Reproduce repeated normal resumes and inspect the response usage block, especially cache_read_input_tokens and cache_creation_input_tokens. Done means identifying the intermittent history-cache failure and confirming that resumed requests retain the expected cached prefix.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
node.js, typescript
Ambito
api, performance
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
48/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.