[BUG] CLI 2.1.145 sends literal "claude-opus-4-7[1m]" as model name on session resume, hits 404, silently falls back to 200K
Summary
Selecting "Opus 4.7 (1M context)" in the /model selector causes Claude Code CLI v2.1.145 to send claude-opus-4-7[1m] verbatim as the model name in the API request body on session resume. Anthropic API returns HTTP 404 (not_found_error: model: claude-opus-4-7[1m]). The CLI then silently falls back to bare claude-opus-4-7 (200K) for all subsequent traffic in the resumed session.
Environment
- OS: macOS 25.4.0 (Darwin), arm64
- Claude Code CLI: v2.1.145 (
claude --versionconfirms) - Plan: Max
- Reproduces: every
claude --resumeof a session that was originally created with 1M-context selection (orclaude --continue). Also reproduces on/compact-triggered SessionStart hook execution path. - No suppressing env vars set:
CLAUDE_CODE_DISABLE_1M_CONTEXT— unsetCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC— unsetANTHROPIC_MODEL— unset~/.claude/settings.jsonhas nomodelfield set (selection persists elsewhere in CLI internal state).
Evidence (intercepted via local HTTP proxy at ANTHROPIC_BASE_URL=http://127.0.0.1:8787)
Request body
{
"model": "claude-opus-4-7[1m]",
"messages": [/* 1 message, ~9 tokens (probe request) */]
}
Request headers
anthropic-beta: claude-code-20250219,oauth-2025-04-20,context-1m-2025-08-07,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05
anthropic-version: 2023-06-01
user-agent: claude-cli/2.1.145 (external, cli)
x-stainless-arch: arm64
x-stainless-os: MacOS
x-stainless-package-version: 0.94.0
x-stainless-runtime: node
x-stainless-runtime-version: v24.3.0
Note: context-1m-2025-08-07 beta header IS correctly included.
Response (HTTP 404)
{
"type": "error",
"error": {
"type": "not_found_error",
"message": "model: claude-opus-4-7[1m]"
},
"request_id": "req_011CbE6iCwMt41bR967dEEQG"
}
Confirmation: silent fallback to 200K
Across the same 24-hour window on this machine:
- 9 requests sent
claude-opus-4-7[1m]→ all returned 404 - 5697 requests subsequently sent bare
claude-opus-4-7→ all succeeded
Each session resume produces exactly one 404 probe followed by ~hundreds of bare-name requests. The probe failure is invisible to the user — no error surfaces in the CLI; subsequent requests just use the 200K variant.
Steps to reproduce
- Run
/modeland select "Opus 4.7 (1M context)" in a Claude Code CLI v2.1.145 session. - Exit the session.
- Resume with
claude --resume <session-id>(or justclaude --resumeand pick the session from the menu). Same behavior occurs when/compacttriggers SessionStart hooks. - Inspect outgoing HTTP traffic to
api.anthropic.com(or use a local intercepting proxy). - Observe first probe request body contains
"model": "claude-opus-4-7[1m]"and receives 404. - Run
/context— denominator is/200k, not/1m.
Root cause hypothesis
The [1m] suffix appears to be a Claude Code internal alias for the 1M-context variant. The CLI should strip the suffix before serializing the model field into the API request body, and rely on the context-1m-2025-08-07 beta header (which IS being sent correctly) to enable the 1M window on the bare claude-opus-4-7 model.
v2.1.145 appears to be sending the bracketed alias literally — Anthropic API does not recognize claude-opus-4-7[1m] as a valid model name.
Tested workaround that did NOT work
Tried the workaround from <https://github.com/anthropics/claude-code/issues/34143#issuecomment-4241018253>:
{
"model": "claude-opus-4-7[1m]"
}
placed in ~/.claude/settings.json. After restart, behavior unchanged — /context still shows /200k and proxy logs confirm the same 404-then-fallback pattern. Possible I entered it incorrectly; happy to retry if there's a known-good schema.
Related issues
- #55504 ("Opus 4.7 ([1m] variant) capped at 200K context in Claude Code Desktop on Max plan") — potentially the same root cause from the Desktop surface. That report is currently flagged for auto-close as duplicate but no underlying issue has been closed/fixed; the API-level evidence in this report may help identify the actual mechanism.
- #50803 (
--modelflag silently drops[1m]suffix) — related, different code path. That report attributes the bug to launcher flag handling; this report shows the same[1m]corruption occurs through/model+--resumewith no flag involvement. - #53031 (silent direction swap to
[1m]on 200K selection, v2.1.119) — opposite direction symptom, may share underlying alias-handling code.
What's unique in this report vs the existing ones
- Raw API evidence — 404 status code, exact error message body, and request payload. Existing reports speculate about cause based on
/contextoutput; this one shows the API rejection directly. - Specific version — v2.1.145 (latest as of filing).
- macOS CLI — existing reports are Windows or Desktop.
- Resume-specific trigger —
claude --resumeand/compactSessionStart hooks both reproduce. Fresh sessions may not exhibit it (haven't tested fresh-session path exhaustively). anthropic-betaheader is correct — rules out beta-header malformation as cause; the bug is specifically in the model field serialization.
Happy to provide additional proxy captures, full request/response dumps, or test specific candidate fixes if helpful.
Showing cached comments. Read the full discussion on GitHub ↗
6 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Additional observations from continued testing on v2.1.145, macOS:
/modelmenu shows a synthesized 5th entry in the bugged stateIn a session that has fallen into the 200K-fallback state, running
/modelshows 5 entries instead of the expected 4. Item 5 is a synthesized entry exposing the raw model ID (no[1m]suffix) and is marked current.Bugged state (
/modelafter resume):Expected state (
/modelon a healthy session):Strong evidence the CLI is holding split internal state: the "Default" entry implicitly maps to
claude-opus-4-7[1m], but on resume the live resolution silently falls to bareclaude-opus-4-7(200K) and the UI surfaces that as a synthesized 5th entry. The user's original selection is no longer represented as "Default" because the bracketed-alias path is broken.--modelCLI flag workarounds that do NOT workTested at launch on v2.1.145:
None of these route the session to the 1M variant.
--model claude-opus-4-7[1m]in bash — partial success, "limbo" stateIn bash (zsh requires quoting — see footnote), the bracketed-alias flag form passes through to the CLI:
Both launch successfully and
/contextreports/1m— so the 1M context window IS active. But the session is in a degraded "limbo" state:/modelshows the same 5-entry menu with the synthesizedclaude-opus-4-7entry selected as current (not "Default").Proxy capture during this state shows what's happening underneath. When
--model claude-opus-4.7[1m](dot form) is used, the CLI strips the[1m]suffix but keeps the dot form and sendsclaude-opus-4.7(dot, no suffix) in the request body. Anthropic API returns:After this wave of 404s, the CLI falls back again to bare
claude-opus-4-7(dash, no suffix). The 1M context window stays active because thecontext-1m-2025-08-07beta header is still being sent — the API appears to honor 1M on the baseclaude-opus-4-7model purely from the beta header. So the user gets 1M context "by accident" while the CLI thinks it's running a different (broken) model, hence the UI warnings and auto-mode unavailability.The one workaround that fully works
In an already-launched REPL on a bugged session:
After selection,
/contextreports/1m, the menu collapses back to the expected 4 entries, auto-mode becomes available, and no "Opus 4 updated" warning. The fix persists only for the current session — resuming again re-corrupts the state.zsh globbing note (not a Claude Code bug)
This is zsh's glob behavior (
[1m]parsed as a character class matching1orm). Quoting fixes it:Mentioning only because it surfaced during testing and might confuse other repro attempts. Not relevant to the underlying bug — bash and quoted-zsh both reach the same "limbo" state described above.
Summary of failure modes observed
| Launch path |
/context| UI state ||---|---|---|
|
claude --resume(from[1m]-selected session) |/200k| 5-entry menu, item 5 selected ||
--model opus,--model claude-opus-4-7, etc. |/200k| 5-entry menu ||
--model claude-opus-4-7[1m](bash or quoted-zsh) |/1m| 5-entry menu with synthesized item 5; "Opus 4 updated" / "Newer version available" warning; auto-mode unavailable or shown as not-supported depending on shell ||
/model→ "Default (recommended)" in REPL |/1m| clean 4-entry menu, auto-mode available |The exact label and effort-support indicator on the synthesized 5th entry varies across launch paths (
Opus 4,Opus 4.7,Opus 4.7 (1M context), with various "Newer version available" / "Effort not supported" annotations), and varies between bash and quoted-zsh invocations of the same command. The variation itself is evidence that the CLI's internal model resolver is in a poorly-defined state when any[1m]path is exercised — different code branches surface different stale catalog entries. Happy to capture exhaustive variations if useful.The 1M context window is reachable via beta header alone on bare
claude-opus-4-7. The CLI's only correct path to set up that header + clean UI state is the in-REPL/model→ "Default" selection. Every other path corrupts the internal model state to some degree.I can confirm this really annoying issue is still going with v2.1.150
Still present on v2.1.154, macOS, Max. The
[1m]naming convention carried forward to Opus 4.8 — the 1M selection's internal model id is nowclaude-opus-4-8[1m](same[1m]suffix as the 4.7 case here), so the same leak path applies. Another reporter above confirmed v2.1.150, so it's spanned at least 2.1.145 → 2.1.154.One narrowing datapoint from testing today: the bug is not triggered by the headless/print path. Driving a resume through a local logging proxy (
ANTHROPIC_BASE_URL=http://127.0.0.1:8788):claude -p "…" --output-format json(create) → request body"model":"claude-opus-4-8"→ 200claude --resume <id> -p "…" --output-format json(resume) → request body"model":"claude-opus-4-8"→ 200So
-p/headless resume sends the clean model name and succeeds. The 404 + 200K fallback reproduces on the interactive TUI resume /--continue/ compact-SessionStart paths, not the print path — which points at the interactive resume code path rebuilding the model string from the persisted/modeldisplay label rather than the headless request builder.Filed #63396 for a related but distinct class of post-context-op request-construction 400s (system role at
messages[0]; modified signedthinkingblocks) seen in the same session-continuity area on 2.1.154.Great forensic work — the resume-time 404 probe + silent fallback pattern is exactly the kind of bug that's invisible without intercepting the wire.
A couple of observations on your evidence:
context-1m-2025-08-07inanthropic-betabutmodel: claude-opus-4-7[1m]in the body strongly suggests two separate code paths are responsible for header and body. The header is set in a "request prep" layer that knows about the 1M feature flag, while the body model string is interpolated in a different layer that's losing the suffix cleanup. Probably a regression in how the model name is sanitized between the/modelpicker state and the API request body — yourclaude --resumeand/compactSessionStart paths probably both share that one sanitization step.claude-opus-4-7[1m]as valid and the upstream API doesn't. The validator and the model registry disagree.export ANTHROPIC_MODEL=claude-opus-4-7-1mor setmodelin~/.claude/settings.jsonexplicitly — both bypass the/modelpicker state and should avoid the[1m]suffix getting added. (You saidANTHROPIC_MODELis unset, but worth trying the settings.json form.)For anyone else reading this and wondering how to intercept like you did: I use ccglass for the same purpose (local proxy on the loopback). It's open source MIT and gives you a dashboard + log of every request body. Yours sounds like it does the same job — but if you want the prompt/tool_call content too (not just the model field), ccglass persists the full body to a queryable log.
Filed with high hopes this gets fixed — the silent fallback is the kind of thing users will blame on "the model got worse" when really it's 200K context all along.
Follow-up: found the mechanism, and reproduced fresh on the current release (Claude Code 2.1.201, Opus 4.8).
The proxy in my original capture (
127.0.0.1:8787) is the Headroom proxy this environment routes through viaANTHROPIC_BASE_URL.Docs explain why a raw
[1m]-suffixed model string can reach the API unmodified behind a gateway (#setting-your-model):So behind a proxy, Claude Code skips its usual model-name validation and passes the literal string through, assuming the gateway resolves aliases itself. Headroom is a wire-compression proxy, not an alias-resolving gateway — it just forwards bytes to the real Anthropic API. A literal
claude-opus-4-8[1m]string reaching that API unmodified → 404 → silent fallback to 200K.Confirmed both directions (interactive session, current release):
ANTHROPIC_BASE_URLpointed at Headroom: create-with-1M → close → resume → silently drops to 200k. Matches this report.ANTHROPIC_BASE_URLunset: same flow keeps 1M correctly.Also re-tested the headless
--print/--resume/--session-idpath directly (not the interactive TUI) on 2.1.201, still behind Headroom, for both Sonnet 5 and Opus 4.8 — neither reproduces;contextWindowstays1000000across an explicit create+resume in both cases. So this is specific to the interactive session/picker path, not the general resume mechanism, and not model-specific.Related: #74562 — same class of bug (a Headroom-style proxy breaking
[1m]handling), different symptom shape:sonnet[1m]there fails to apply the window at all rather than reverting on resume.100% reproducible through the proxy, 0% without it — I'd lean toward keeping this open. Claude Code should either resolve
[1m]aliases before handing off to a non-default base URL, or fail loudly instead of silently downgrading context.Edit: this likely isn't Opus-specific either. The "passes any string through without checking it" behavior quoted above is model-agnostic — any
[1m]-suffixed alias should hit the same literal-passthrough → 404 → silent-fallback path behind a non-translating proxy. #74562 already reportssonnet[1m]/fable[1m]failing to apply the window there too, just surfacing on first selection rather than specifically on resume — plausibly the same underlying bug, different trigger point, not independently confirmed via the exact resume-then-404 sequence for those two.