Skip to content

[Bug] Claude Code accounts native Anthropic models (Sonnet, Opus) at 200k through ocx claude, despite the registry declaring 1M (ANTHROPIC_MODEL_CONTEXT_WINDOWS) #5755

Closed

Description

@ChrisSpotta

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 1

Result (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 1

Result (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)

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

  1. added
    bugSomething isn't working
    catalogModel catalog, slugs, visibility, routed entries
    cliCLI, config inject, packaging flags
    installInstallation or packaging
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 24, 2026
  2. added
    priority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,
    on Sep 24, 2026
  3. ChrisSpotta commented on Sep 24, 2026

    @ChrisSpotta
    Author

    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 claude resolves 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:

    1. Picked kimi-k3 (moonshot) in the interactive /model picker. The gateway discovery response (GET /v1/models with anthropic-version: 2023-06-01 against the proxy) serves it as claude-opus-4-8-olm with max_input_tokens: 1048576 — correct at this layer.
    2. After the first exchange, the session's actual model id canonicalizes to claude-ocx-moonshot--kimi-k3 (visible in the running session and saved as the default model in ~/.claude.json / ~/.claude/settings.json).
    3. The discovery response contains no claude-ocx-* ids at all (33 entries, all claude-opus-4-8-* slot aliases) — and no claude-sonnet-5 / claude-opus-5-5 either.
    4. Claude Code resolves the context window for the id it is actually running (claude-ocx-moonshot--kimi-k3) against a list that doesn't contain it → built-in 200k fallback. Live /context in that session: 89.8k/200k tokens (45%), Auto-compact window: 200k tokens — on a model whose true window is 1,048,576.
    5. Same shape confirmed for deepseek-flash (deepseek) (session id claude-ocx-deepseek--deepseek-flash; declared 1048576 in ocx claude config status; live fallback to 200k) and glm-5.3-flash (zai-api) (session id claude-ocx-zai-api--glm-5.3-flash; additionally its picker-slot discovery entry claude-opus-4-8-ibk serves max_input_tokens: null, so GLM loses its window at both layers).
    6. Meanwhile ocx claude config status shows correct contextWindows.* 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-5 for native Anthropic), and Claude Code falls back to 200k for any id it cannot find.

    Practical impact: every ocx claude session currently runs at a 200k effective window regardless of model choice — the /model picker 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 json invocations report unrecognized_model + contextWindow: 200000 for 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.

  4. FredAmartey commented on Sep 24, 2026

    @FredAmartey
    Contributor

    I measured this against a local fake gateway with Claude Code 2.1.281. Claude Code ignores max_input_tokens from /v1/models and caches only id and display_name. Behind a gateway it counts 200k for any model id without [1m] and 1M for any id with it. Its discovery request sends User-Agent: claude-code/2.1.281, so opencodex serves the readable ocx-claude-* ids plus a · 1M row 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 ignores CLAUDE_CODE_MAX_CONTEXT_TOKENS for ids that start with claude-.

  5. ChrisSpotta commented on Sep 24, 2026

    @ChrisSpotta
    Author

    Independent confirmation of @FredAmartey's mechanism, on the same install as the original report (opencodex 2.64.0, Claude Code 2.1.281):

    ENABLE_CLAUDEAI_MCP_SERVERS=false \
    ocx claude -p "Reply with exactly the string SONNET_1M_SUFFIX_TEST_OK and nothing else." \
      --model "sonnet[1m]" --strict-mcp-config --output-format json --max-turns 1
    "modelUsage": {
      "claude-sonnet-5[1m]": {
        "contextWindow": 1000000,
        "maxOutputTokens": 128000,
        "canonicalModel": "claude-sonnet-5",
        "provider": "firstParty"
      }
    }

    is_error: false, exact sentinel returned. Plain --model sonnet on the same install, same day, reported contextWindow: 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 id claude-ocx-moonshot--kimi-k3[1m] (suffix present); the suffix-less canonical ids all showed 200k.

    Two notes for other readers hitting this:

    1. The default experience is still broken as filed — plain --model sonnet / --model opus (and picker selections that canonicalize to suffix-less legacy claude-ocx-* ids saved by older releases) resolve to 200k unless the user knows to add [1m] or pick the · 1M row. So this is a workaround, not a fix; the discovery/canonicalization gap in the original report stands.
    2. If your saved default model predates the fix: re-select the model in /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 autoCompactWindow override I described earlier, since the [1m]-suffixed saved default makes it redundant — will confirm /context still shows 1M on the next fresh session and report back if it does not.

  6. FredAmartey commented on Sep 24, 2026

    @FredAmartey
    Contributor

    For the default case, ocx claude already marks the tier slots it's configured with. With claudeCode.tierModels.sonnet set to claude-sonnet-5 and .opus to claude-opus-5-5, it exports ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-5[1m] (same for Opus), so plain --model sonnet resolves to the 1M variant. I checked both halves: the env opencodex builds, and Claude Code 2.1.281 mapping --model sonnet through it. Whether that should be on without any config is @lidge-jun and @Ingwannu's call.

  7. ChrisSpotta commented on Sep 24, 2026

    @ChrisSpotta
    Author

    Root cause traced from the installed 2.64.0 source — including why @FredAmartey's tierModels fix does not cover installs like this one (no anthropic provider 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 sonnet through ocx claude still reports contextWindow: 200000 on this install — the [1m] marking never fires.

    Tracing the installed source (@bitkyc08/opencodex@2.64.0):

    1. effectiveModelEnv (src/claude/context-windows.ts:200) maps tierModels.sonnet → ANTHROPIC_DEFAULT_SONNET_MODEL, but the value passes through withOneMillionMarker(value, windows, auto) first.
    2. withOneMillionMarker (:180) only appends [1m] when windows[bareSelector(selector)] passes shouldMarkOneMillion (:117), which returns false for undefined — per its own doc comment, "unknown selectors stay untouched".
    3. That windows map is fetched from the running daemon's management API (call path in src/cli/claude.ts) and built by buildClaudeContextWindows(nativeSlugs, routedModels, …) (context-windows.ts:123) — i.e. from configured-provider catalog rows.
    4. This install has no anthropic provider configured (ocx provider list: openai, qwen-local, minimax, deepseek, xiaomi-mimo, moonshot, zai-api only). ocx claude passes Anthropic traffic through using the Claude Code OAuth credential (authFoundBy: claude-json-oauth) with no catalog rows for Anthropic at all.
    5. Consequence: the daemon-served windows map contains no claude-sonnet-5 / claude-opus-5-5 entries (matches ocx claude config status showing zero Anthropic contextWindows.* keys) → withOneMillionMarker can'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/models response 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 anthropic provider. The registry already knows these windows (e.g. #3521, #5577); they just don't flow into buildClaudeContextWindows for 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: hasOneMillionMarker short-circuits before the windows lookup — a pre-suffixed selector passes through untouched, which is also why · 1M picker rows behave. And why Fred's verification passed on his install: his windows map isn't starving.)

  8. ChrisSpotta commented on Sep 24, 2026

    @ChrisSpotta
    Author

    Closing the loop on my earlier note: with the autoCompactWindow override reverted, a fresh ocx claude session on the [1m]-suffixed saved default (claude-ocx-moonshot--kimi-k3[1m]) shows no 200k regression — /context reports 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.

  9. FredAmartey commented on Sep 25, 2026

    @FredAmartey
    Contributor

    You're right. My check built the window map with an anthropic provider 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].

  10. Ingwannu commented on Sep 25, 2026

    @Ingwannu
    Owner

    Maintainer decision: the 1M marker should work by default for the known 1M native Anthropic tiers used by ocx claude; requiring users to configure an anthropic provider 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 configured anthropic row, 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:

    • seed the known native Anthropic tier context facts independently of whether an Anthropic provider row is configured, or otherwise emit the equivalent marked native tier defaults;
    • preserve explicit user environment variables and explicit claudeCode.tierModels precedence;
    • add [1m] only for models with authoritative >=1M metadata; do not mark Haiku or unknown/custom selectors;
    • avoid a global CLAUDE_CODE_MAX_CONTEXT_TOKENS override, since that changes unrelated models and compaction behavior;
    • cover an empty/default config with no Anthropic provider, explicit tier overrides, user-wins environment behavior, and the 200k Haiku boundary.

    The reported [1m] workaround and the fresh-session /context confirmation establish the mechanism, but the issue should remain open until the default no-provider path is fixed and tested.

  11. devin-ai-integration commented on Sep 25, 2026

    @devin-ai-integration
    Contributor

    New matched pull request: #5826 (would resolve this issue) — fix(claude): mark native Claude tiers 1M without an anthropic provider [priority: P2]

  12. added a commit that references this issue on Sep 25, 2026
    bcdbfc6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entriescliCLI, config inject, packaging flagsinstallInstallation or packagingpriority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,toolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions