[BUG] `--resume` drops the Opus `[1m]` modifier, invalidating the prompt cache

Resolved 💬 2 comments Opened Jun 6, 2026 by EmpireJones Closed Jul 13, 2026

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

  1. 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"

  1. Resume it, with the active model still non-[1m]:

claude --resume <id> --settings '{"model":"claude-opus-4-8"}' -p "hi"

  1. Compare the model used and the cache reuse across the two turns.
  2. Observe the first turn runs claude-opus-4-8[1m], the resumed turn runs claude-opus-4-8, and the resumed turn reuses 0 cached tokens.
  3. 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 init event's model field (the resolved model for that turn)
  • the final result event's usage.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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗