anthropics / anthropics/claude-code
[BUG] Usage "Detailed Breakdown" over-counts cache read/write/input ~2.5× — sums usage per content-block line, not per API request
- Vorherrschende Sprache
- Python
- Sterne
- 145k
- Forks
- 23.1k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
### Environment
- Claude Code desktop app (Code tab), v2.1.270
- Surface: the Usage pill under the composer → **Detailed Breakdown**
### Summary
The per-session **Detailed Breakdown** (Input / Output / Cache read / Cache write) counts token usage **once per transcript content-block entry** instead of **once per API request**.
A normal assistant turn is written to the session `.jsonl` as several lines — one per content block (`thinking`, `text`, `tool_use`) — and **every one of those lines carries the identical `usage` object** (same `requestId`, byte-for-byte the same token counts). The Breakdown sums per line, so each request's usage is multiplied by its number of content blocks (~2.5 on average, 3 for a typical tool-calling turn).
Net effect: **Cache read — the dominant figure — is inflated ~2.5×**, which makes it misleading.
### Impact
Cache read dominates token volume, so a 2.5× overstatement makes the headline number actively confusing (a session that truly read ~168M cache tokens is shown as ~418M). If the session **Cost** estimate is derived from the same per-line sum, it's overstated too. The 5-hour / weekly **limit bars** appear unaffected (presumably computed server-side from real usage) — so the Breakdown disagrees with the bars, compounding the confusion.
### Evidence (one real session)
The Breakdown as displayed, vs. the two ways of summing the transcript's `usage` metadata:
| Field | Breakdown UI | Summed per **API request** (`requestId`) | Summed per **transcript line** |
|-------------|-------------:|-----------------------------------------:|-------------------------------:|
| Input | 1.6k | 629 | **1,571** |
| Cache read | 418M | 168,204,894 | **418,001,293** |
| Cache write | 5.5M | 2,004,667 | **5,539,964** |
The UI matches the **per-line** sum on all three fields to displayed precision.
Session shape: **315 unique API requests, 787 assistant lines carrying `usage`.** 184 requests appear 3× in the transcript (thinking+text+tool_use), 53 appear 2×, 61 once → weighted average ≈ 2.5, matching the 2.49× inflation (168M → 418M). For **every** repeated request (254/254), the `usage` fields are **byte-identical** across its lines — the same request's usage is being re-counted, not distinct reads.
### Root cause hypothesis
The Breakdown aggregates `message.usage` per transcript entry. It should **dedupe by `requestId` (or `message.id`) and count each request's `usage` once.** `cache_read_input_tokens`, `cache_creation_input_tokens`, and `input_tokens` describe a single API request; duplicating them per content block multi-counts them.
### Minimal repro
Run against any session transcript (`~/.claude/projects//.jsonl`):
```python
import json, sys
from collections import defaultdict
by = defaultdict(list)
for line in open(sys.argv[1]):
try: o = json.loads(line)
except: continue
if o.get('type') != 'assistant': continue
u = (o.get('message') or {}).get('usage')
if not u: continue
rid = o.get('requestId') or o['message'].get('id')
by[rid].append(u.get('cache_read_input_tokens', 0))
naive = sum(v for xs in by.values() for v in xs)
actual = sum(xs[0] for xs in by.values()) # identical across a request's lines
print(f"per-line (what the UI shows): {naive:,}")
print(f"per-request (correct): {actual:,}")
print(f"inflation: {naive/actual:.2f}x")
```
**Expected:** the Breakdown shows the per-request total (~168M here). **Actual:** it shows the per-line total (~418M).
### Notes
- **Output** does *not* follow the per-line rule — the UI showed 4.3k, well below even the true per-request output (~475K) — so it has separate handling and is likely a distinct bug.
- **Subagents** are tracked separately in the UI ("What's using your limits"), and run their own transcripts, so the figures above are main-thread only.
Beitragsleitfaden
Für dieses Repository ist kein Beitragsleitfaden indexiert
Rechercherichtung
Start with a session .jsonl transcript and the supplied Python repro, then trace the aggregation behind the Usage pill’s Detailed Breakdown. Verify how requestId or message.id is handled and compare the displayed totals with per-request sums. Done means cache read, cache write, and input usage are counted once per API request and the Breakdown matches the expected total.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- cli, developer-experience
- Issue-Typ
- Bug
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 58/100