Skip to content

Investigate _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL (do not ship by default) #16

Description

@jellologic

Finding

Claude Code decides "is this a first-party endpoint?" like this — verified in 2.1.216:

function Wd(){ if(Z._CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL) return !0; return K9n() }
function K9n(){ let e=process.env.ANTHROPIC_BASE_URL; if(!e) return !0; return jPe(e) }
function jPe(e){ try{ let t=new URL(e).host; return ["api.anthropic.com"].includes(t) }catch{ return !1 } }

Two things stand out:

  1. The allowlist is exactly one host. Any gateway, proxy, or third-party endpoint fails it — that's the root of #46416.
  2. _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL force-passes the gate. Leading underscore, undocumented, and the only underscore-prefixed variable in the entire binary — a strong signal it's internal/testing, not a supported knob.

Wd() gates a lot: native 1M context (eM), gateway model discovery, default and small-fast model selection, model-picker rows, CLAUDE_CODE_PROPAGATE_TRACEPARENT, and PBr() which returns "third_party_provider".

Also verified — provider identity is env-driven and independent of the URL gate:

function Tn(){ if(dy())return"gateway"; return Z.CLAUDE_CODE_USE_BEDROCK?"bedrock":  :"firstParty" }

Setting ANTHROPIC_BASE_URL alone leaves Tn() === "firstParty" while Wd() returns false. Two independent switches, and we should reason about them separately rather than assuming one implies the other.

Why this is not an obvious win

Flipping this makes Claude Code believe a third-party endpoint is Anthropic's. That plausibly unlocks 1M context and model discovery — but it also means first-party-only behaviour gets applied to a backend that may not support it: beta headers, experimental fields, adaptive thinking. That is precisely the class of 400 we are fixing in #3 (Extra inputs are not permitted, Input tag 'adaptive' found).

So this could trade one silent breakage for a louder one. It may also cause Claude Code to skip compatibility fallbacks it would otherwise apply.

An undocumented underscore-prefixed internal flag can also change or vanish in any release, with no deprecation path.

Task — investigate before adopting

  • Test against a real z.ai / OpenRouter key with the flag on: does /status report the true context window?
  • Does gateway model discovery start working?
  • Does it introduce 400s that don't occur with it off?
  • Compare against the [1m] suffix (BUG: z.ai preset silently runs at 200K context instead of 1M #14) — if that alone gets us the context window, this is unnecessary risk
  • Check whether it alters auth or telemetry behaviour in ways we don't want

Recommendation

Do not ship this by default. If it proves useful, put it behind an explicit opt-in (e.g. assumeFirstParty: true) with the tradeoff documented. [1m] (#14) is the supported, documented mechanism and should remain our primary lever.

Metadata

Metadata

Assignees

No one assigned

    Labels

    compatThird-party gateway compatibility

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions