[BUG] Unexpected session model tokens usage for opus
Preflight Checklist
- [x] I have searched existing issues 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?
Seems that when you use a session with opus-A and running the commands using opus-B they have some complicated relations. Context copying or something. But you are paying for it.
My case was:
When running session on opus47, command on sonnet and subagents on haiku:
Total cost: $0.96
Total duration (API): 6m 48s
Total duration (wall): 4m 4s
Total code changes: 118 lines added, 0 lines removed
Usage by model:
claude-sonnet-4-6: 366 input, 14.0k output, 638.6k cache read, 48.4k cache write ($0.58)
claude-haiku-4-5: 265 input, 18.1k output, 741.4k cache read, 165.2k cache write ($0.3712)
And this is what I expect to see ^
When running session on opus47, command on opus46, and subagents on haiku:
Total cost: $2.08
Total duration (API): 7m 51s
Total duration (wall): 3m 34s
Total code changes: 120 lines added, 0 lines removed
Usage by model:
claude-opus-4-6: 1.5k input, 6.7k output, 295.6k cache read, 35.5k cache write ($0.54)
claude-haiku-4-5: 266 input, 19.6k output, 746.1k cache read, 168.3k cache write ($0.3830)
claude-opus-4-7: 532 input, 8.9k output, 1.0m cache read, 65.9k cache write ($1.15)
basically command run is the most expensive thing here.
When running session on opus46, command on opus47, and subagents on haiku:
Total cost: $1.58
Total duration (API): 5m 17s
Total duration (wall): 2m 27s
Total code changes: 121 lines added, 0 lines removed
Usage by model:
claude-opus-4-7: 528 input, 2.7k output, 108.1k cache read, 72.0k cache write ($0.57)
claude-haiku-4-5: 253 input, 18.9k output, 715.9k cache read, 160.5k cache write ($0.3671)
claude-opus-4-6: 356 input, 4.9k output, 447.8k cache read, 46.9k cache write ($0.64)
Same thing here, just cheaper.
This should be fixed or properly explained in the documentation.
What Should Happen?
I should see usages by command and subagents models, not by session model.
Error Messages/Logs
no
Steps to Reproduce
- make a command running subagents and specify opus-A
- make subagent using haiku
- start a session, switch to opus-B
- run the command
- cost shows strange numbers for the session model
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.143
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
iTerm2
Additional Information
_No response_
8 Comments
This is a clean instance of a broader cost-surface pattern that has accumulated across the issue tracker over the past 48 hours — nine separate issues, all
area:cost, all from different reporters, all describing the same general structure: the billing counter and the operator's expected work-product diverge silently.In your specific case, the divergence is mixed-model overhead. The opus-4.7 ↔ opus-4.6 ↔ haiku path produces ~2.17× the cost of the opus-4.7 ↔ sonnet-4.6 ↔ haiku path for what looks like comparable work — and the existing UI does not expose the multiplier at command-issue time. The decision to invoke a command on opus-4.6 instead of sonnet-4.6 cost you ~$1.12 extra on this single run, with no warning until
Total costprinted at the end.Five operator-side detections that would have caught this before the run completed (and that I sketched in a free synthesis of the broader cluster, here):
--modelenforcement that refuses to invoke a different model forcommandor subagents unless the cost-multiplier was acknowledged in writing.claude /usage --jsonafter every N tool calls and compare against the declared model spend; flag drift past a threshold.cache_readvsinputvsoutputvscache_writeper model in real time, since cache_write is where the mixed-model handoff hides most of the cost.Your data point ($2.08 mixed-opus path vs $0.96 sonnet path, same workload) is the cleanest single number I've seen for #1 — the cost multiplier is large enough that most operators will accept losing some quality if they see the dollar number at compose time rather than at run end.
A few related cases in the same cluster window (different leak surfaces, same general structure): #60283 (10+ turns of re-reading in-context files with zero output), #60228 (38% → 100% usage gauge spike in 30 seconds with no observable load), #60331 / #60328 / #60223 (
/ultrareviewdecrements free-run quota on crash before findings). The Gist linked above catalogues all nine and maps each to a specific operator-side detection.If you have a
claude /usage --jsonsnapshot from the affected sessions, that's the most useful artifact for vendor-side reproduction.@yurukusa i don't but i can reproduce and capture any diagnostics devs need.
This is not a question of cost difference between models set. The question is why model not used in the workflow spends anything at all. And this happens only with opus. Probably there is even overhead when session is 4.6 and command 4.6, numbers are growing but hard to say for sure because of normal deviations.
@hurricup — you're right, my framing was off. The load-bearing observation in your data is the asymmetry between cases A and B, not the gross cost difference. In case A (session=opus-4.7, command=sonnet-4.6) opus-4.7 is absent from the usage block entirely. In case B (session=opus-4.7, command=opus-4.6) opus-4.7 shows 532 input + 8.9k output + 1.0M cache read + 65.9k cache write — more billed than the command model itself. The opus-4.7 line in case B is the entire bug; the opus-4.6 line is what the operator authorised. With opus↔sonnet the session is effectively dormant during command execution; with opus↔opus it isn't.
Three candidate mechanisms that would produce that opus-family-specific asymmetry:
command on opus-4.6workload with session models at varying context depths (empty / mid / near-compact). If opus-4.7's billed tokens scale with session context size, the mechanism is replay. If roughly constant, it's a fixed supervisor turn (mechanism 3).cache_readtoken count against the maximum the command model alone could have read during the run. If it exceeds, the cache is double-attributed.~/.claude/projects/*/*.jsonlduring case B and look for opus-4.7 entries that aren't tied to a user-issued tool call.The structural framing for the wider cluster: this is a different leak surface from the nine I catalogued earlier (gauge drift, model selection cost, retry storms, in-context re-reads, etc.). Those are operator-observable at the time the work is happening. Yours is operator-observable only at run-end, in the
Usage by modelblock, and only when the operator already knows which model ran which turn. The remediation belongs at the harness/UI layer — surface the session-model spend per command turn in real time, not as a post-run lump. Worth its own catalog entry; happy to cross-link if you want to extend the constellation.Tried:
no effect
Tried to run command 100% faster than 5 mins, still the same:
So no idea what is this read/write about.
Logs analysis says that opus47 loads something in the end. presumably some opus46 output. but why and why it does not do this with sonnet.
@hurricup — the 1m45s repro is the data point that narrows the candidate set. With wall-time under the 5-minute cache TTL and
ENABLE_PROMPT_CACHING_1H=1confirmed not to alter the pattern, two of the three mechanisms I proposed earlier are now less likely; the third sharpens into something testable.Reading your 1m45s usage block more carefully:
The token shape is diagnostic. 8 input tokens means the harness is passing the session model a near-empty fresh prompt. 720 output tokens means the session model is producing a modest response (not a full agentic continuation, not a one-line ack — something in between, roughly the size of a structured summary or a fold-in instruction). 92.3k cache_read on essentially-empty input means the session model is reading 92.3k tokens worth of cached context — almost certainly the command model's output (or the command turn's full transcript) being re-presented to opus-4.7 for ingestion. 21.1k cache_write means opus-4.7 is then materialising a fresh cache entry of comparable size, which is the shape of a summarisation/fold-in turn writing the result back into session memory.
That pattern is structurally consistent with mechanism 3 (Phantom supervisor turn), but with a specific refinement: it's not a pre-command preparation turn, it's a post-command fold-in turn. The session model runs after the command model completes, reads the command's transcript via cache, and writes a synthesised version back into session memory so that the next user turn sees the work as if the session model had done it. With opus↔sonnet the families diverge and the fold-in is either skipped or executed cheaply enough to not show on the usage block; with opus↔opus the lineage is close enough that the harness routes the fold-in through the full session model.
Diagnostic command to confirm. Tail your session JSONL during a fresh opus↔opus reproduction and look for an opus-4.7 turn after the opus-4.6 command turn closes:
If you see an opus-4.7 entry with the same token shape (small input, small output, large cache_read, smaller cache_write) timestamped after the opus-4.6 entry that corresponds to the command, the post-command fold-in mechanism is confirmed. If the opus-4.7 entry is timestamped before, mechanism 1 (pre-command replay) is back on the table. If you can't find an opus-4.7 entry that maps to either, mechanism 2 (cache topology bleed at the billing layer, not at the inference layer) becomes the working hypothesis.
Implication for the cluster. If post-command fold-in is the mechanism, the operator-side mitigation is uncomfortable but available: avoid same-family session/command pairings until the fold-in either becomes optional or its cost is surfaced before authorisation. The harness-side fix is to either (a) skip the fold-in when the command model's output is small enough to be passed through cheaply, (b) route the fold-in through a smaller model in the same family, or (c) surface the fold-in's projected cost in the command-tool permission prompt so the operator can authorise it explicitly. Today the operator is opted into the fold-in by virtue of choosing the command model; they cannot opt out without changing model families.
For the wider quota-leakage cluster I catalogued earlier, this is a 10th distinct leak surface — the first one where the leakage is family-conditioned rather than triggered by operator action. I'll add it to that gist with cross-reference to this Issue once you confirm or refute the post-command-fold-in hypothesis with the JSONL tail.
The 92.3k cache_read on 8 fresh-input tokens is the cleanest billing-layer signature I've seen for this shape of mechanism. Worth filing as its own diagnostic axis regardless of which of the three candidates is the correct one.
— @yurukusa
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
I'm sorry, how does it work here? I filled the issue with a problem. Which is still there. I see no reaction from devs, so why is it closed for inactivity?
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.