Gateway model discovery silently fails when auth comes from apiKeyHelper
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_DISCOVERYopt-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"
}
}
- Launch
claude ~/.claude/cache/gateway-models.jsonis never written;/modelshows no gateway entries~/.claude/debug/latestcontains 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗