Skip to content

Gateway model discovery silently fails when auth comes from apiKeyHelper #56675

Description

@ericwinsor-aisi

Summary

When ANTHROPIC_BASE_URL points at a custom gateway and authentication is provided only via apiKeyHelper (no ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY env var), gateway /v1/models discovery never runs and gateway models never appear in the /model picker.

Environment

  • Claude Code 2.1.131, native installer (linux-x64 Bun-compiled binary at ~/.local/share/claude/versions/2.1.131)
  • Build: GIT_SHA c122cd1bd5247f2a4bde8bcaec712fb1dff247e8, BUILD_TIME 2026-05-06T06:09:35Z
  • Also reproduces on 2.1.129 (first version with the CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY opt-in)

Reproduction

// /etc/claude-code/managed-settings.json (or user settings)
{
  "apiKeyHelper": "/usr/local/bin/some-key-helper.sh",
  "env": { "ANTHROPIC_API_KEY": "" }
}
// ~/.claude/settings.json
{
  "env": {
    "ANTHROPIC_BASE_URL": "http://127.0.0.1:5757",
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
  }
}
  1. Launch claude
  2. ~/.claude/cache/gateway-models.json is never written; /model shows no gateway entries
  3. ~/.claude/debug/latest contains no [gatewayDiscovery] lines, and shows [Bootstrap] Skipped: no usable OAuth or API key (same race)

Root cause

Identifiers below are minified names from the 2.1.131 linux-x64 bundle (/$bunfs/root/src/entrypoints/cli.js, SHA above). Source can be located by grepping for the string literals "[gatewayDiscovery]" and "gateway-models.json".

The discovery function (minified Dr7, contains all [gatewayDiscovery] log strings) is called fire-and-forget during startup init and guards on having a token or key before fetching:

let $ = process.env.ANTHROPIC_AUTH_TOKEN, q = sX();
if (!$ && !q) return;          // ← bails here, no log line

sX() resolves the API key; for the apiKeyHelper branch it returns xr8(), which is a synchronous cache read:

function xr8() { return Hx?.value ?? null }

Hx is only populated when the async helper shell-out (FCH(), execFile of the configured script) resolves. Nothing on the init path awaits it before discovery runs, so Hx is still null, q is null, and discovery returns silently before ever calling fetch(${baseUrl}/v1/models?limit=1000).

Suggested fix

Either await the helper in the discovery function (FCH() already returns the key):

let q = sX() ?? await FCH();

…or drop the auth guard for this fetch — many gateways don't require auth on GET /v1/models, and the header construction already handles the no-key case (...$?{Authorization:...}:q?{"x-api-key":...}:{}).

Workaround

Pre-write ~/.claude/cache/gateway-models.json (the picker reader only checks the env-flag gate and baseUrl match, not whether the fetch succeeded), or set a placeholder ANTHROPIC_AUTH_TOKEN and tolerate the "Auth conflict" startup warning.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions