Repository navigation
[Bug] Claude Code accounts native Anthropic models (Sonnet, Opus) at 200k through ocx claude, despite the registry declaring 1M (ANTHROPIC_MODEL_CONTEXT_WINDOWS) #5755
Description
Client or integration
Claude Code
Area
Proxy and routing (possibly Catalog / models — see "Root cause guess" below)
Summary
Launching Claude Code through ocx claude and selecting --model sonnet (or --model opus) causes Claude Code to account 200,000 tokens as the model's context window, instead of the 1,000,000 tokens both Claude Sonnet 5 and Claude Opus 5.5 actually have and that Claude Code itself uses when not routed through the proxy. Confirmed on both models — this is a defect in how ocx claude reports context for the anthropic provider generally, not a Sonnet-specific issue. This is a same-model, same-machine, same-CLI-version, back-to-back comparison — the only variable is whether ANTHROPIC_BASE_URL points at the OpenCodex proxy or at Anthropic directly.
This is not a hypothetical: the total_cost_usd/modelUsage telemetry Claude Code itself emits in --output-format json reports the discrepancy directly, per-call, with no interpretation needed.
The practical effect: any ocx-code-style workflow (Claude Code launched via ocx claude, no non-Anthropic model override) runs its main session at 1/5 of Sonnet's real context budget, triggering /compact roughly 5x more often than it should.
Reproduction
Two bounded, single-turn, no-tool calls, back to back, same machine, same Claude Code binary (2.1.281):
1) Routed through OpenCodex (ocx claude, mirrors the real ocx-code launch shape):
ENABLE_CLAUDEAI_MCP_SERVERS=false \
ocx claude -p "Reply with exactly the string SONNET_CTX_TEST_OK and nothing else." \
--model sonnet --strict-mcp-config --output-format json --max-turns 1Result (relevant fields only):
"modelUsage": {
"claude-sonnet-5": {
"contextWindow": 200000,
"maxOutputTokens": 128000,
"canonicalModel": "claude-sonnet-5",
"provider": "firstParty"
}
}2) Native, no proxy (same shell, ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY unset):
env -u ANTHROPIC_BASE_URL -u ANTHROPIC_AUTH_TOKEN -u CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY \
claude -p "Reply with exactly the string NATIVE_CTX_TEST_OK and nothing else." \
--model sonnet --output-format json --max-turns 1Result (relevant fields only):
"modelUsage": {
"claude-sonnet-5": {
"contextWindow": 1000000,
"maxOutputTokens": 128000,
"canonicalModel": "claude-sonnet-5",
"provider": "firstParty"
}
}Both calls: is_error: false, stop_reason: end_turn, exact expected sentinel returned, canonicalModel identical (claude-sonnet-5), provider identical (firstParty). The only difference between the two invocations is the presence of the opencodex proxy in the network path.
Retested after upgrading to 2.64.0 (npm install -g @bitkyc08/opencodex@2.64.0, confirmed via ocx --version, proxy restarted via ocx stop && ocx start and confirmed listening on :10100 before retesting):
ENABLE_CLAUDEAI_MCP_SERVERS=false \
ocx claude -p "Reply with exactly the string SONNET_CTX_RETEST_264_OK and nothing else." \
--model sonnet --strict-mcp-config --output-format json --max-turns 1"modelUsage": {
"claude-sonnet-5": {
"contextWindow": 200000,
"maxOutputTokens": 128000,
"canonicalModel": "claude-sonnet-5",
"provider": "firstParty"
}
}Bug reproduces identically on 2.64.0 (the latest release at time of writing). This rules out "already fixed in the newest release" as an explanation — it is a current, live defect, not a version-lag artifact of testing on 2.63.0.
Also confirmed on Opus (same 2.64.0 install, same proxy instance):
ENABLE_CLAUDEAI_MCP_SERVERS=false \
ocx claude -p "Reply with exactly the string OPUS_CTX_TEST_OK and nothing else." \
--model opus --strict-mcp-config --output-format json --max-turns 1"modelUsage": {
"claude-opus-5-5": {
"contextWindow": 200000,
"maxOutputTokens": 128000,
"canonicalModel": "claude-opus-5-5",
"provider": "firstParty"
}
}Identical failure shape, different model, different canonical id (claude-opus-5-5 vs. claude-sonnet-5). This confirms the defect is scoped to the anthropic provider path in ocx claude as a whole, not to any one model.
Scope note — this is confined to the ocx claude (Claude Code) integration. OpenCodex's other primary integration, the native codex CLI, uses a completely separate model catalog (~/.codex/opencodex-catalog.json, referenced from ~/.codex/config.toml) that does not list anthropic/Sonnet/Opus as a provider at all — only OpenAI-native models and the same third-party providers (MiniMax, DeepSeek, Kimi, MiMo) tested elsewhere in this report. So there is no equivalent code path to compare against on the Codex CLI side for Anthropic models specifically; the bug cannot be "the same on both integrations" because Anthropic models are only reachable through ocx claude in the first place. For a model present in both catalogs (MiniMax-M3), the Codex-CLI-facing catalog correctly declares context_window: 1000000 and the CLI picks it up without a fallback warning once the fully-qualified provider-prefixed id is used — so this is not evidence of a broader, catalog-wide problem beyond the Anthropic path.
Also checked: ocx claude config status (full output available on request) enumerates an explicit contextWindows.<key> override for every routed third-party model tested this session (e.g. minimax/MiniMax-M3 → 1000000, moonshot/kimi-k3 → 1048576, deepseek/deepseek-flash → 1048576, qwen-local/qwen3.8-27b → 114688, plus their claude-opus-4-8-* slot-alias and claude-ocx-* alias mirrors) — but no contextWindows.* line exists for sonnet, claude-sonnet-5, or any Anthropic-provider key at all. Whether that's expected (native Anthropic rows may not be meant to appear in this particular status view) or a symptom of the same gap is not established here — flagging it as a secondary observation, not a claimed root cause.
Root cause guess (not verified against the running instance's actual source/build)
A repo devlog entry found while investigating — devlog/_fin/260802_wt3_provider_wire/030_bug_c_claude_1m_windows.md (consolidating #839/#854) — documents that ANTHROPIC_MODEL_CONTEXT_WINDOWS in src/providers/registry.ts already includes "claude-sonnet-5": 1_000_000 as of that fix (landed ~2026-08-02, months before this report), alongside claude-fable-5, claude-opus-5, claude-opus-4-8 at 1M and claude-haiku-4-5 at 200k. Per that same devlog, src/claude/model-info.ts routes Anthropic-provider models through the routedModels loop (not the OpenAI-specific nativeSlugs loop that issue #1218 fixed), pulling context window from m.contextWindow — which should ultimately trace back to this same registry map.
Given that, claude-sonnet-5 (and, confirmed separately, claude-opus-5-5) served through ocx claude reporting 200k contradicts the documented, already-landed design — this reads as either:
- a regression since that fix landed,
- a gap specific to how the short model aliases (
sonnet,opus) resolve at runtime vs. the canonical ids the registry map keys on (e.g. if alias resolution routes through a generic/shared slot that doesn't carry the per-model registry fact through to what Claude Code's gateway-discovery response actually serves), or - a local install/config drift issue (stale generated catalog artifact not regenerated across a version bump) specific to this environment rather than a
main-branch defect.
Confirming the same symptom on two separate Anthropic models (Sonnet and Opus, both via their short aliases) makes the second theory — an alias-resolution gap shared by both short-alias lookups — the most likely of the three, since a per-model regression would more plausibly hit one model, not both uniformly. But this report still does not claim which of these it is — only that the live calls above demonstrate the symptom cleanly and consistently, and that it contradicts the codebase's own stated intent as of the cited devlog.
Related reports (corroborating pattern, not duplicates)
- [Bug] Claude Code shows 200k context for native OpenAI models (gpt-5.6/5.5): model-info.ts omits contextWindow; NATIVE_GPT56_CONTEXT_WINDOW stale at 372k #1218 — "Claude Code shows 200k context for native OpenAI models (gpt-5.6/5.5)" — same exact symptom shape (a first-party/native model losing its context-window fact somewhere between the registry and Claude Code's
/v1/modelsdiscovery response, silently falling back to Claude Code's built-in 200k default) but for OpenAI's native passthrough (src/claude/model-info.ts'snativeSlugsloop), not Anthropic's own models. Confirmed fixed onmain(currentmodel-info.tspassesnativeMaxInput ?? nativeWindowintomodelInfo()for that loop). This report is a plausible sibling instance of the same failure class in a different code path, not the same bug. - mimo (token plan) registry entry declares no model-level capacity facts — V2.6 rows published without context window, output cap, or modalities #5695 — mimo token-plan registry entry missing model-level capacity facts entirely for V2.6 rows (closed 2026-09-24, fix on
dev, not yet released). - [Provider compatibility] zhipu-bigmodel-coding/glm-5.3-flash loses contextWindow in live catalog on v2.54.0 #4570 —
zhipu-bigmodel-coding/glm-5.3-flashlosing itscontextWindowdue to an all-or-nothing operator-override merge bug inenrichProviderFromRegistry(src/providers/derive.ts) — same general "registry declares it, something downstream drops it" shape, different provider and different specific bug.
None of these three name anthropic/claude-sonnet-5 specifically, so this is filed as a new report rather than a duplicate.
Version
- opencodex: reproduced on both 2.63.0 and 2.64.0 (latest release at time of writing, released 2026-09-23). Upgrading did not change the result — see the 2.64.0 retest and Opus confirmation in "Reproduction" above.
- Claude Code: 2.1.281
- Node: not re-captured this session (see prior verification doc in this same
docs/reviews/directory for a matching environment snapshot)
Operating system
Debian GNU/Linux 12 (bookworm), x86_64
Provider and model
anthropic / claude-sonnet-5 (requested via --model sonnet) and claude-opus-5-5 (requested via --model opus) — both confirmed affected.
Logs or error output
No error — this is a silent metadata/accounting discrepancy, not a request failure. Both calls returned is_error: false and the exact expected output. The discrepancy is visible only in the modelUsage.<model>.contextWindow telemetry field, reproduced verbatim above.
Screenshots and supporting files
None. Full JSON output of both calls is quoted in full above (only unrelated high-entropy fields — session_id, uuid — were left in as-is since they carry no secret material; no tokens or credentials appear in either payload).
Redacted configuration
ocx claude config status output referenced above is available in full on request; the fields quoted here (contextWindows.* key list, absence of any Anthropic-provider key) are non-secret routing metadata.
Checks
- I searched existing issues and documentation (see "Related reports" above; not claimed exhaustive — see the parent verification session's broader search notes).
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
ChrisSpotta commentedon Sep 24, 2026
Follow-up evidence — the defect is broader than the title suggests, and the mechanism is now pinned down (same install, opencodex 2.64.0, Claude Code 2.1.281).
Every model served through
ocx clauderesolves to 200k in-session — including third-party models whose context windows opencodex itself declares correctly. The Anthropic case in the original report is one instance of a unified bug, not a separate registry problem.Verified live, end to end:
kimi-k3 (moonshot)in the interactive/modelpicker. The gateway discovery response (GET /v1/modelswithanthropic-version: 2023-06-01against the proxy) serves it asclaude-opus-4-8-olmwithmax_input_tokens: 1048576— correct at this layer.claude-ocx-moonshot--kimi-k3(visible in the running session and saved as the default model in~/.claude.json/~/.claude/settings.json).claude-ocx-*ids at all (33 entries, allclaude-opus-4-8-*slot aliases) — and noclaude-sonnet-5/claude-opus-5-5either.claude-ocx-moonshot--kimi-k3) against a list that doesn't contain it → built-in 200k fallback. Live/contextin that session:89.8k/200k tokens (45%),Auto-compact window: 200k tokens— on a model whose true window is 1,048,576.deepseek-flash (deepseek)(session idclaude-ocx-deepseek--deepseek-flash; declared1048576inocx claude config status; live fallback to 200k) andglm-5.3-flash (zai-api)(session idclaude-ocx-zai-api--glm-5.3-flash; additionally its picker-slot discovery entryclaude-opus-4-8-ibkservesmax_input_tokens: null, so GLM loses its window at both layers).ocx claude config statusshows correctcontextWindows.*entries keyed by exactly the canonical ids sessions run as (e.g.contextWindows.claude-ocx-moonshot--kimi-k3: 1048576) — the data exists and is correctly keyed, but whatever generates the gateway discovery response is not drawing from it for these ids.Unified root cause, as far as external observation can take it: sessions canonicalize to model ids the gateway discovery response never serves (
claude-ocx-<provider>--<model>for routed models,claude-sonnet-5/claude-opus-5-5for native Anthropic), and Claude Code falls back to 200k for any id it cannot find.Practical impact: every
ocx claudesession currently runs at a 200k effective window regardless of model choice — the/modelpicker cannot be used to work around the Anthropic case, because the third-party alternatives fail the same lookup one step later.Headless note:
claude -p --output-format jsoninvocations reportunrecognized_model+contextWindow: 200000for all of these canonical/slot ids even where the picker path has a correctly-served entry, so headless telemetry understates things further — possibly a second, related gap in the SDK query path.FredAmartey commentedon Sep 24, 2026
I measured this against a local fake gateway with Claude Code 2.1.281. Claude Code ignores
max_input_tokensfrom/v1/modelsand caches onlyidanddisplay_name. Behind a gateway it counts 200k for any model id without[1m]and 1M for any id with it. Its discovery request sendsUser-Agent: claude-code/2.1.281, so opencodex serves the readableocx-claude-*ids plus a· 1Mrow for every model at 1M or more. Picking that row, or--model sonnet[1m]/--model opus[1m], gives the full window.claude-ocx-*is the spelling from older releases, probably a saved selection, and Claude Code also ignoresCLAUDE_CODE_MAX_CONTEXT_TOKENSfor ids that start withclaude-.ChrisSpotta commentedon Sep 24, 2026
Independent confirmation of @FredAmartey's mechanism, on the same install as the original report (opencodex 2.64.0, Claude Code 2.1.281):
is_error: false, exact sentinel returned. Plain--model sonneton the same install, same day, reportedcontextWindow: 200000— so the[1m]suffix on the id is indeed the switch, for native Anthropic through the proxy as well as for routed models. This also matches the interactive observations from my earlier comment: the session that healed to 1M was running idclaude-ocx-moonshot--kimi-k3[1m](suffix present); the suffix-less canonical ids all showed 200k.Two notes for other readers hitting this:
--model sonnet/--model opus(and picker selections that canonicalize to suffix-less legacyclaude-ocx-*ids saved by older releases) resolve to 200k unless the user knows to add[1m]or pick the· 1Mrow. So this is a workaround, not a fix; the discovery/canonicalization gap in the original report stands./model(or launch once with an explicit[1m]id) — the saved selection keeps the old spelling until you do.For my own setup I have reverted the
autoCompactWindowoverride I described earlier, since the[1m]-suffixed saved default makes it redundant — will confirm/contextstill shows 1M on the next fresh session and report back if it does not.FredAmartey commentedon Sep 24, 2026
For the default case,
ocx claudealready marks the tier slots it's configured with. WithclaudeCode.tierModels.sonnetset toclaude-sonnet-5and.opustoclaude-opus-5-5, it exportsANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-5[1m](same for Opus), so plain--model sonnetresolves to the 1M variant. I checked both halves: the env opencodex builds, and Claude Code 2.1.281 mapping--model sonnetthrough it. Whether that should be on without any config is @lidge-jun and @Ingwannu's call.ChrisSpotta commentedon Sep 24, 2026
Root cause traced from the installed 2.64.0 source — including why @FredAmartey's
tierModelsfix does not cover installs like this one (noanthropicprovider configured).First, the empirical check: with
claudeCode.tierModels.sonnet = "claude-sonnet-5"(and.opus = "claude-opus-5-5") set in~/.opencodex/config.json, plain--model sonnetthroughocx claudestill reportscontextWindow: 200000on this install — the[1m]marking never fires.Tracing the installed source (
@bitkyc08/opencodex@2.64.0):effectiveModelEnv(src/claude/context-windows.ts:200) mapstierModels.sonnet→ANTHROPIC_DEFAULT_SONNET_MODEL, but the value passes throughwithOneMillionMarker(value, windows, auto)first.withOneMillionMarker(:180) only appends[1m]whenwindows[bareSelector(selector)]passesshouldMarkOneMillion(:117), which returnsfalseforundefined— per its own doc comment, "unknown selectors stay untouched".windowsmap is fetched from the running daemon's management API (call path insrc/cli/claude.ts) and built bybuildClaudeContextWindows(nativeSlugs, routedModels, …)(context-windows.ts:123) — i.e. from configured-provider catalog rows.anthropicprovider configured (ocx provider list:openai,qwen-local,minimax,deepseek,xiaomi-mimo,moonshot,zai-apionly).ocx claudepasses Anthropic traffic through using the Claude Code OAuth credential (authFoundBy: claude-json-oauth) with no catalog rows for Anthropic at all.claude-sonnet-5/claude-opus-5-5entries (matchesocx claude config statusshowing zero AnthropiccontextWindows.*keys) →withOneMillionMarkercan't mark → env goes out suffix-less → Claude Code's 200k fallback. The identical starvation explains the discovery gap in the original report (no Anthropic ids in the/v1/modelsresponse either) — one root, two symptoms.So the gap is: Anthropic window facts never reach the Claude windows map when Anthropic is used via passthrough OAuth without a configured
anthropicprovider. The registry already knows these windows (e.g. #3521, #5577); they just don't flow intobuildClaudeContextWindowsfor provider-less installs. A fix presumably needs to seed Anthropic's registry windows into that map regardless of provider configuration.(Why the
[1m]workaround still works regardless:hasOneMillionMarkershort-circuits before the windows lookup — a pre-suffixed selector passes through untouched, which is also why· 1Mpicker rows behave. And why Fred's verification passed on his install: his windows map isn't starving.)ChrisSpotta commentedon Sep 24, 2026
Closing the loop on my earlier note: with the
autoCompactWindowoverride reverted, a freshocx claudesession on the[1m]-suffixed saved default (claude-ocx-moonshot--kimi-k3[1m]) shows no 200k regression —/contextreports an ~830k auto-compact window (Claude Code's default headroom below the 1M the marker accounts), Claude Code 2.1.282. So the suffix alone carries the session; no override needed.FredAmartey commentedon Sep 25, 2026
You're right. My check built the window map with an
anthropicprovider row in it, so it only holds on installs that configure one. Without one, the map only has OpenAI natives and configured providers, and the tier slots go out without[1m].Ingwannu commentedon Sep 25, 2026
Maintainer decision: the 1M marker should work by default for the known 1M native Anthropic tiers used by
ocx claude; requiring users to configure ananthropicprovider just to preserve Claude Code's native context accounting is not an acceptable default.The current code confirms the gap described above.
buildClaudeContextWindows()is populated from visible OpenAI-native slugs plus configured routed providers. With no configuredanthropicrow,effectiveModelEnv()has no window evidence for the Sonnet/Opus tier selectors and emits no marked tier default, even though the registry already knows those native Anthropic models are 1M. Claude Code then falls back to 200k for the suffix-less id.The fix should stay narrow:
claudeCode.tierModelsprecedence;[1m]only for models with authoritative >=1M metadata; do not mark Haiku or unknown/custom selectors;CLAUDE_CODE_MAX_CONTEXT_TOKENSoverride, since that changes unrelated models and compaction behavior;The reported
[1m]workaround and the fresh-session/contextconfirmation establish the mechanism, but the issue should remain open until the default no-provider path is fixed and tested.devin-ai-integration commentedon Sep 25, 2026
New matched pull request: #5826 (would resolve this issue) — fix(claude): mark native Claude tiers 1M without an anthropic provider [
priority: P2]fix(claude): mark native Claude tiers 1M without an anthropic provider (