[BUG] `--resume` drops the Opus `[1m]` modifier, invalidating the prompt cache
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?
Starting a session with --model claude-opus-4-8[1m] (the 1M-context Opus) and then resuming it with --resume does not keep the [1m] modifier.
On resume the base model is kept from the session, but the [1m] modifier is taken from the current model setting. So if the active model is anything without [1m] — plain Opus, or Sonnet/Haiku picked in another session — the resumed turn silently runs without [1m].
This happens easily in normal use: start an Opus [1m] session, switch to a different model (e.g. Sonnet or Haiku) in a new session, then resume the original — it comes back as plain Opus, not [1m].
Because the model string changes between the first turn (claude-opus-4-8[1m]) and the resumed turn (claude-opus-4-8), the prompt cache is invalidated and the resumed turn reuses 0 cached tokens.
What Should Happen?
--resume should keep the full model string the session was created with, including [1m], so the resumed turn reuses the cached prefix instead of dropping it.
Error Messages/Logs
Steps to Reproduce
- Start a
[1m]session with a large--append-system-prompt(so a cache block is created), with the active model set to plain Opus:
claude --session-id <id> --model "claude-opus-4-8[1m]" --settings '{"model":"claude-opus-4-8"}' --append-system-prompt "<~8k tokens>" -p "hi"
- Resume it, with the active model still non-
[1m]:
claude --resume <id> --settings '{"model":"claude-opus-4-8"}' -p "hi"
- Compare the model used and the cache reuse across the two turns.
- Observe the first turn runs
claude-opus-4-8[1m], the resumed turn runsclaude-opus-4-8, and the resumed turn reuses 0 cached tokens. - Repeat with the active model set to
claude-opus-4-8[1m]on the resume and the resumed turn keeps[1m]and reuses the cache (~100%).
(The --settings model stands in for "the active model isn't [1m]", and it's the resume's one that matters — e.g. you picked Sonnet/Haiku in another session. In real use you'd just claude --resume <id> with that model active.)
What metadata to check — run with --output-format stream-json --verbose and read:
- the
initevent'smodelfield (the resolved model for that turn) - the final
resultevent'susage.cache_read_input_tokens/usage.cache_creation_input_tokens
The same fields are in the session transcript at ~/.claude/projects/*/<session>.jsonl.
Simple script to reproduce — two scenarios differing only in the active model:
#!/usr/bin/env python3
"""`claude --resume` drops the `[1m]` modifier passed via `--model`, breaking prompt-cache
reuse on resume. The two scenarios below differ only in the settings model:
settings = claude-opus-4-8 -> resume loses [1m] -> 0% cache reuse (bug)
settings = claude-opus-4-8[1m] -> resume keeps [1m] -> 100% cache reuse (works)
"""
import json, subprocess, tempfile
from uuid import uuid4
opus = "claude-opus-4-8"
def run(base, extra, cwd):
events = [json.loads(l) for l in subprocess.run(
base + extra + ["hi"], cwd=cwd, check=True,
capture_output=True, text=True).stdout.splitlines()]
label = "resume" if "--resume" in extra else "first "
print(f" {label} model used =", next(e["model"] for e in events if e.get("subtype") == "init"))
return next(e["usage"] for e in events if e.get("type") == "result")
def scenario(settings_model):
session = str(uuid4())
base = ["claude", "-p", "--output-format", "stream-json", "--verbose",
"--settings", f'{{"model":"{settings_model}"}}', "--setting-sources", "project",
"--strict-mcp-config", "--tools", "", "--append-system-prompt", "cache-marker " * 1500]
print(f"settings model = {settings_model!r}")
with tempfile.TemporaryDirectory() as cwd:
first = run(base, ["--session-id", session, "--model", f"{opus}[1m]"], cwd)
resumed = run(base, ["--resume", session], cwd)
avail = first["cache_read_input_tokens"] + first["cache_creation_input_tokens"]
read = resumed["cache_read_input_tokens"]
print(f" resume reused {read}/{avail} ({read / avail:.1%})\n")
scenario(opus) # bug
scenario(f"{opus}[1m]") # control
output:
settings model = 'claude-opus-4-8'
first model used = claude-opus-4-8[1m]
resume model used = claude-opus-4-8
resume reused 0/8758 (0.0%)
settings model = 'claude-opus-4-8[1m]'
first model used = claude-opus-4-8[1m]
resume model used = claude-opus-4-8[1m]
resume reused 8757/8757 (100.0%)
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.167
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
VS Code integrated terminal
Additional Information
The 2.1.144 changelog says "Resumed sessions now keep the model they were using instead of picking up another session's /model choice." That fix keeps the base model, but seems to have missed the [1m] modifier — it still isn't kept, so resuming an Opus [1m] session after selecting another model elsewhere loses [1m] and its cache.
More generally: the prompt cache is model-scoped, so any model change on resume invalidates the cached prefix. The bug is that [1m] is dropped here unintentionally.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗