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

Abierto
#94,224 0 comentarios 0 reacciones 0 asignados Ver en GitHub
area:cost bug has repro
Lenguaje dominante
Python
Estrellas
145k
Forks
23.1k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
cli, developer-experience
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Activo
Claridad
Bastante claro
Aptitud para principiantes
58/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.