[BUG] `--resume` drops the session's `--effort` level, invalidating the prompt cache

Open 💬 7 comments Opened Jun 7, 2026 by EmpireJones

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?

Start a session with --effort low, then resume it with --resume. The resumed turn does not run at low — it runs at whatever effort the resuming invocation resolves to.

On resume, effort is taken from the current invocation, not from the session. If --effort is omitted on the resume, the turn falls back to the built-in default effort; if --effort is set to a different level, that level is used. Either way the session's low is lost. (This is proven by the control below: re-passing --effort low on the resume restores full cache reuse, while omitting it does not.)

This happens easily in normal use: start a session at --effort low, then claude --resume <id> without re-passing --effort — it comes back at the default effort, not low.

Because the resumed request now differs from the request that created the session — same model, same --append-system-prompt, but a different effort — the prompt cache is invalidated. In the repro below, cache reuse on the resumed turn drops to ~29% (the session's cached prefix is largely rewritten), even though the only input that changed is the effort level.

What Should Happen?

--resume should restore the effort the session was created with, so the resumed turn reuses the cached prefix instead of rewriting it.

Claude Code already does this for the model: the 2.1.144 changelog notes that "Resumed sessions now keep the model they were using instead of picking up another session's /model choice." --effort is the same class of per-session generation setting — claude --help describes it as the "Effort level for the current session" — so resume should preserve it the same way, rather than silently re-resolving it from the current invocation.

Error Messages/Logs

Steps to Reproduce

  1. Start a session with a large --append-system-prompt (so a cache block is created) at --effort low:

claude --session-id <id> --model "claude-sonnet-4-6" --effort low --append-system-prompt "<~8k tokens>" -p "hi"

  1. Resume it without re-passing --effort:

claude --resume <id> --model "claude-sonnet-4-6" --append-system-prompt "<~8k tokens>" -p "hi"

  1. Compare the cache reuse across the two turns.
  2. Observe the first turn writes the cache and the resumed turn reuses only ~29% of it, re-creating the rest.
  3. Repeat with --effort low also on the resume and the resumed turn reuses ~100% of the cache.

The model is identical on both turns (claude-sonnet-4-6) and the --append-system-prompt is byte-for-byte the same, so this is not the [1m]/model-drop issue — the only input that differs between the two turns is the effort level.

What metadata to check — run with --output-format stream-json --verbose and read:

  • 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. Effort itself is not recorded anywhere in the transcript or the init event — the per-request usage block logs service_tier and speed but no effort field — so the cache-reuse delta is the only observable tell that the resumed turn ran at a different effort.

Simple script to reproduce — two scenarios differing only in whether the resume re-passes --effort:

#!/usr/bin/env python3
"""`claude --resume` drops the `--effort` level, breaking prompt-cache reuse on resume.
The two scenarios below differ only in whether the resume re-passes --effort:

    resume without --effort      -> resume loses effort -> ~29% cache reuse  (bug)
    resume with --effort low     -> resume keeps effort -> 100% cache reuse  (works)

claude-sonnet-4-6 so the model is identical on both turns and the only variable is
effort (sonnet supports effort low/medium/high; max is Opus-tier only)."""
import json, subprocess, tempfile
from uuid import uuid4

model = "claude-sonnet-4-6"
base = ["claude", "-p", "--output-format", "stream-json", "--verbose",
        "--model", model, "--setting-sources", "project", "--strict-mcp-config",
        "--tools", "", "--append-system-prompt", "cache-marker " * 1500]


def run(extra, cwd):
    out = subprocess.run(base + extra + ["hi"], cwd=cwd, check=True,
                         capture_output=True, text=True).stdout
    events = [json.loads(l) for l in out.splitlines()]
    return next(e["usage"] for e in events if e.get("type") == "result")


def scenario(label, resume_extra):
    session = str(uuid4())
    with tempfile.TemporaryDirectory() as cwd:
        first = run(["--session-id", session, "--effort", "low"], cwd)
        resumed = run(["--resume", session] + resume_extra, cwd)
    avail = first["cache_read_input_tokens"] + first["cache_creation_input_tokens"]
    read = resumed["cache_read_input_tokens"]
    print(f"{label}: resume reused {read}/{avail} ({read / avail:.1%})")


scenario("resume WITHOUT effort (bug) ", [])                  # bug
scenario("resume WITH --effort low    ", ["--effort", "low"]) # control

output:

resume WITHOUT effort (bug) : resume reused 2136/7396 (28.9%)
resume WITH --effort low    : resume reused 7397/7397 (100.0%)

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.168

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

This is the effort-level analog of the [1m]-modifier resume bug (#65805), with a difference in mechanism. For [1m], resume restores the base model from the transcript but drops the recorded [1m] modifier (a partial restore of recorded state). For effort, there is nothing recorded to restore from — effort isn't written to the session, so resume always takes it from the current invocation. In both cases the resumed request ends up differing from the request that created the session, so the cached prefix is invalidated.

The partial reuse on resume (~29% above) is the portion of the prefix ahead of where effort perturbs the request; everything after it is re-created. (Contrast the [1m] bug, where the model string changes at the very front of the prefix and reuse drops to 0%.) Re-passing the same --effort on resume restores 100% reuse, which is what isolates effort as the sole cause.

More generally: because the session's effort is not preserved across resume, the resumed turn silently runs at a different effort whenever --effort is omitted (or set differently) — and any such effort change invalidates the cached prefix. The fix is to persist the session's effort and reapply it on --resume/--continue, exactly as the model now is per the 2.1.144 changelog.

View original on GitHub ↗

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