anthropics / anthropics/claude-code

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

Đang mở
#92,938 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
area:agent-sdk area:core area:cost duplicate platform:windows
Ngôn ngữ chính
Python
Star
145k
Fork
23.1k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

### 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_

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
node.js, typescript
Lĩnh vực
api, performance
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
48/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.