opusplan downgrades plan mode to Sonnet past 200k — Claude Code regression (it previously auto-compacted at 200k and kept 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?
With --model opusplan on long sessions, two connected regressions show up in recent Claude Code versions:
1. New plans are generated by Sonnet once context exceeds ~200k. In plan mode, as soon as the session passes ~200k tokens, plan-mode responses are produced by Sonnet instead of Opus. The status bar reflects it (e.g. Sonnet 4.6 · 204.2k/200k — over 100% of the shown 200k), but nothing signals this is an opusplan downgrade rather than a deliberate choice. On the earlier Claude Code version — the one whose opusplan used Opus 4.7 for plan mode — plan mode stayed on Opus and did not downgrade.
2. Plan execution now runs on Sonnet with up to ~1M context. On the latest Claude Code version, after exiting plan mode the plan is executed on Sonnet with context allowed to grow up to ~1M. In previous Claude Code versions, plan execution ran on Sonnet capped at ~200k.
What Should Happen?
Plan mode under opusplan must stay on Opus — keeping Opus for planning is the entire point of the preset, so the switch to Sonnet should not happen at all, and the planning model should never be silently swapped mid-session.
Two ways to fix it:
- Restore the earlier Claude Code behavior: auto-compact at ~200k when running
opusplan, so context never crosses the threshold and Opus is retained in plan mode. - Or add an explicit
opusplan[1m]preset that embraces the 1M window — Opus[1m] in plan mode (no downgrade to Sonnet) and Sonnet[1m] for plan execution.
Error Messages/Logs
Steps to Reproduce
claude --model opusplanon the current Claude Code version (opusplan uses Opus 4.8 for plan mode).- Grow a plan-mode session past ~200k tokens.
- Plan-mode replies are now produced by Sonnet; status bar shows e.g.
Sonnet 4.6 · 204.2k/200k.
(On the earlier Claude Code version — the one that used Opus 4.7 for plan mode — the same session auto-compacted at ~200k and stayed on Opus.)
Claude Model
Other
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.162
Platform
Other
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
Regression across Claude Code versions: on the earlier Claude Code version — the one whose opusplan used Opus 4.7 for plan mode — context was compacted at ~200k in both modes. Plan mode therefore stayed on Opus (it never crossed the threshold), and execution ran on Sonnet capped at ~200k. On the current Claude Code version (opusplan now uses Opus 4.8 for plan mode) this 200k compaction no longer fires — execution runs on Sonnet with the full ~1M window, and plan mode, once context exceeds 200k, downgrades from Opus to Sonnet.
Mechanism (reverse-engineered from the bundled CLI JS — symbol names paraphrased, treat as a lead rather than gospel): the plan-mode model selector keeps Opus only while an "exceeds 200k tokens" guard is false; that guard compares the most recent assistant turn against a hardcoded 200000 literal. Once usage is no longer compacted below 200k, the guard trips and plan mode falls back to Sonnet.
Why this is counterintuitive: choosing opusplan is an explicit request for Opus-quality planning, yet the longer/more complex the session — exactly when Opus matters most — the more certain the fallback to Sonnet. The status bar shows the active model, but nothing connects it to an opusplan policy downgrade, so it reads as expected behavior.
After the downgrade the session continues on Sonnet with the ~1M window, so context keeps accumulating toward ~800k.
Workaround that restores Opus planning (re-creates the earlier Claude Code behavior): set CLAUDE_CODE_AUTO_COMPACT_WINDOW=200000 so context is compacted before the 200k guard trips. Note CLAUDE_CODE_DISABLE_1M_CONTEXT=1 alone is not sufficient — only the auto-compact window actually caps usage below the threshold.
6 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Not a duplicate — same architectural class ("silent Opus→Sonnet downgrade"), distinct surface.
/model opus[1m]max-effort sessions (not plan mode, not the opusplan preset) plus a separate/compact-non-execution bug. Different trigger, different threshold.Novel here and not in any of the above: (1) opusplan plan-mode Opus→Sonnet specifically tied to the disappearance of the 200k auto-compact; (2) plan execution now runs on Sonnet with the full ~1M window instead of the previous ~200k cap; (3) reverse-engineered guard + working workaround (
CLAUDE_CODE_AUTO_COMPACT_WINDOW=200000); (4) concrete fix proposals (restore auto-compact, or add an explicitopusplan[1m]preset). Keeping open as a distinct regression. 👎 to prevent auto-close.Confirmed fixed in v2.1.172 — thanks! 🙏
The fix landed in Claude Code v2.1.172. Both launch presets now behave exactly as requested here:
| Launch | Plan mode | Execution |
|---|---|---|
|
claude --model opusplan| Opus 4.8 — 1M, auto-compacts at ~200k | Sonnet 4.6 — 200k ||
claude --model 'opusplan[1m]'| Opus 4.8 — 1M | Sonnet 4.6 — 1M |So the two presets give a clean cost-vs-headroom choice: plain
opusplankeeps Opus on a 1M window for planning while capping Sonnet execution at 200k, andopusplan[1m]puts both on 1M. Either way the>200000guard that caused the Sonnet downgrade in plan mode is no longer tripped.One small non-blocking follow-up:
opusplan[1m]is launch-flag only —/model opusplan[1m]returns "not found", unlike/model opus[1m]/sonnet[1m]. Parity there would be nice, but it's not needed for this issue.Closing as fixed. Thanks for the quick turnaround! 🙏
I am using Bedrock API and already have Sonnet 4.6 1M and Opus 4.8 1M as my default sonnet/opus models. Still, with
opusplan, there is a SILENT switch to Sonnet at 200K when in planning mode. There IS nowopusplan[1m]but it ONLY works via commandline--model, and does NOT work via/modelorsettings.jsonpreset.What's worse - even with
--model 'opusplan[1m]', Claude Code 2.1.190 keeps dropping to Sonnet (not right away, perhaps? I am confused by what is going on and do not want to waste more time and tokens on debugging). With a 200k+ pre-existing session,claude -r [...] --model 'opusplan[1m]', I saw 'Opus 1M' in my statusline after Shift-tabbing to plan mode, but after continuing the session for a bit, I realized the statusline was showing sonnet again :(Seeing this recur on v2.1.216 (newer than the v2.1.172 fix) with the plain
opusplanpreset — notopusplan[1m]— which per the fix table above is the one that's supposed to auto-compact at ~200k specifically so the guard never trips.In a single ~7-hour session,
EnterPlanModewas called 4 times. Model observed on the turns immediately following each call, cross-referenced againstcache_read_input_tokens + cache_creation_input_tokens + input_tokensfrom the transcript at that point:| # | Total context at call | Switched to Opus? |
|---|---|---|
| 1 | ~146,124 tokens | Yes |
| 2 | ~232,502 tokens | No — stayed on Sonnet |
| 3 | ~383,490 tokens | No — stayed on Sonnet |
| 4 | ~471,994 tokens | No — stayed on Sonnet |
The one success is the only call under 200k; all three failures are over it — matches the guard mechanism described above exactly.
The part that seems like a regression of the fix itself, not just the original bug: zero auto-compaction events occurred anywhere in this transcript, despite context growing unchecked from ~146k to ~472k tokens on the plain (non-
[1m])opusplanpreset — which is exactly the case the v2.1.172 fix was supposed to handle by keeping the session compacted below the threshold.settings.jsonhad no explicitautoCompactWindowoverride (default),autoCompactEnabledunset (defaults on).So this doesn't look like the same shape as the original report (guard trips because compaction stopped happening by default) — it looks like auto-compact isn't firing at all for plain
opusplanon this version, silently, with no error or warning either for the missed compaction or the resulting downgrade. Nothing in the product surfaced any of this — it was only caught by manually diffing the transcript'smessage.modelfield against wall-clock time after the fact.Happy to share the raw transcript excerpts (token usage + model per turn) if useful for reproduction.
Follow-up to my last comment — dug further into the same transcript and found what I think is the actual mechanism, not just the symptom.
Re-checked all 4
EnterPlanModecalls from that session directly against the transcript'smessage.usagefields (independent of the earlier table, same numbers):| # | Context at call | Switched to Opus? |
|---|---|---|
| 1 | ~127k | Yes |
| 2 | ~246k | No |
| 3 | ~386k | No |
| 4 | ~479k | No |
Zero exceptions — every call under ~200k switched, every call over it didn't.
The new part: I searched the entire transcript (not just up to the last plan-mode call) for any compaction event, and found exactly one, with a
compactMetadatablock:That's ~10 hours after the last failed switch (20:33 UTC), and it didn't fire until context hit 1.1M tokens — nowhere near the ~200k ceiling the v2.1.172 fix table says plain
opusplanshould auto-compact at. For the entire multi-hour window covering all three failed switches, auto-compaction simply never engaged.So this doesn't look like "auto-compact stopped happening" (my last comment's framing) — auto-compact does still run, just against something much closer to the full 1M context window than the ~200k specifically documented for plain
opusplan. Once context first crosses ~200k, the plan-mode guard trips and then has no way to get relieved until a compaction that arrives 5x too late to matter for that session's plan-mode usage.If it's useful, I have the raw transcript lines around the compaction event (
compactMetadata+isCompactSummary) and can paste the full block rather than just the summary numbers above.