anthropics / anthropics/claude-code

[BUG] Transcript rows zero every usage counter but keep usage.cache_creation — cache_creation_input_tokens != sum(cache_creation)

Open
#95,555 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:core area:cost bug has repro
Dominant language
TypeScript
Stars
146k
Forks
23.8k
PR merge metrics
PR metrics pending

Description

What's wrong

Some assistant rows in session transcripts (~/.claude/projects/**/*.jsonl) have every top-level usage counter set to 0, while usage.cache_creation still holds the real write. That breaks the invariant documented on the prompt-caching page, under 1-hour cache duration: "the current cache_creation_input_tokens field equals the sum of the values in the cache_creation object."

In most cases another row of the same message (same message.id + requestId) carries the correct, consistent usage. So this looks like a row written with a zeroed usage snapshot, not an API response that disagrees with itself.

Example

Two rows of one message, CC 2.1.237, claude-opus-5, both with content: [text]. Usage fields only:

{"input_tokens": 2, "cache_creation_input_tokens": 977, "cache_read_input_tokens": 316685, "output_tokens": 5175, "cache_creation": {"ephemeral_1h_input_tokens": 977, "ephemeral_5m_input_tokens": 0}}
{"input_tokens": 0, "cache_creation_input_tokens": 0,   "cache_read_input_tokens": 0,      "output_tokens": 0,    "cache_creation": {"ephemeral_1h_input_tokens": 977, "ephemeral_5m_input_tokens": 0}}
How often (one machine, 2026-07-10 → 2026-09-19)
  • 540,177 assistant rows carrying usage.cache_creation, across 153,985 distinct messages
  • 153 messages have at least one row where cache_creation_input_tokens != sum(cache_creation.*)
    • in 136 of them, another row of the same message is consistent
    • 17 messages have no consistent row at all. For those, the transcript records a cache write only in the nested object.
  • Direction: almost always cache_creation_input_tokens = 0 with a non-zero split. The largest single row is 417,471 tokens reported as 0.
  • Seen on 20 CC versions from 2.1.205 through 2.1.275, and on claude-opus-5, claude-opus-4-8, claude-fable-5, claude-fable-5-1.
  • Stop reasons end_turn and tool_use. Content types text, thinking, tool_use. None are API-error rows.
Why it matters

Anything that accounts for cache writes from transcripts reads the top-level field and silently undercounts. That includes cost reports and cache-rebuild analysis. Readers that de-duplicate by message id and keep the first row are affected whenever the zeroed row comes first.

Expected

Every row written for a message carries the same usage, or at least one that satisfies the documented invariant.

Repro (read-only)

For each assistant row with message.usage.cache_creation, compare cache_creation_input_tokens with the sum of the cache_creation values. Then group by (message.id, requestId).

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the read-only scan of assistant rows in ~/.claude/projects/**/*.jsonl, comparing cache_creation_input_tokens with the cache_creation sum and grouping by message.id and requestId. Trace the transcript-writing entry point that produces these rows and check how repeated rows receive usage snapshots. Done means written rows preserve the documented invariant and the repro no longer finds inconsistent usage.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.