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

Status Open
Reported on v2.1.144
Maintainer reply None cached
Activity 9 comments · opened Jun 7, 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?

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 ↗

7 Comments

EmpireJones · 2 months ago

Related: [[BUG] --resume drops the Opus [1m] modifier, invalidating the prompt cache](https://github.com/anthropics/claude-code/issues/65805)

github-actions[bot] · 2 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/65805

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

EmpireJones · 2 months ago

Not a duplicate. The 'checklist' says to "file separate reports for different bugs", so I did.

arivero · 2 months ago

is this message related?

Change effort level?
  Your next response will be slower and use more tokens
  
  This conversation is cached for the current effort level. Switching to high means the full history gets re-read...
EmpireJones · 2 months ago
is this message related? `` Change effort level? Your next response will be slower and use more tokens This conversation is cached for the current effort level. Switching to high means the full history gets re-read... ``

Depends on when that text is showing up. When the effort level is changed, it can't use the existing cache (like that message is saying). The reported bug is specifically about it not restoring the proper effort upon resuming.

pek2atx · 1 month ago

Confirming this is still happening, and want to add a more consequential real-world example than cache efficiency alone.

I have a long-running Claude Code session that's been resumed many times over several weeks (terminal closed/reopened repeatedly, claude --resume each time). I set the session to medium effort early on and never touched it again — but weeks later, discovered via direct transcript inspection that the session was actually running at high effort the whole time. Model correctly persisted as claude-sonnet-5 across every resume (matches the fix already shipped for model in 2.1.144), but effort silently reverted to whatever a fresh terminal launch resolves to by default, exactly as this issue describes — I never explicitly changed it, and the only /effort interaction in the entire session transcript was a cancelled one (opened the picker, saw the current — apparently already-wrong — selection, closed it without confirming).

The consequence went beyond wasted cache: high effort generates substantially more tokens per turn (extended thinking). On a long-running session, that inflates the token cost of every turn, which shrinks how much actual conversation the compaction mechanism's preserved-tail window covers (the tail is token-bounded, not message-count-bounded). In my case this contributed to a real information-loss incident — a decision agreed on just a couple of messages before a compaction event fell outside the preserved tail and got lost, because those "couple of messages" were far bulkier in tokens than they'd have been at the effort level I actually intended.

So this isn't just a cache-reuse efficiency bug — for long-running, frequently-resumed sessions, it can silently and cumulatively escalate effort level far beyond what the user set, with downstream consequences for context retention across compaction, not just cost. Given model already got the "resume preserves session state" fix, effort seems like a natural extension of the same fix rather than a separate effort.

EmpireJones · 1 month ago

It looks like the related variant [[BUG] --resume drops the Opus [1m] modifier, invalidating the prompt cache](https://github.com/anthropics/claude-code/issues/65805) was auto-closed.

Showing cached comments. Read the full discussion on GitHub ↗