Summary
On v7.0.3, every Grok model (grok-3, grok-4, grok-4.3, grok-4-fast-*, etc.) fails with Missing Authentication header from the proxy, despite XAI_API_KEY being correctly set in the environment. Direct calls to api.x.ai with the same key succeed, so the key itself is valid — claudish is not threading it onto the outbound request.
Environment
- claudish version: 7.0.3 (
/opt/homebrew/bin/claudish)
- Platform: macOS (Darwin 25.2.0, arm64)
- Auth state:
XAI_API_KEY set (84 chars, no whitespace, validated). ANTHROPIC_API_KEY unset (OAuth login via Claude Code).
Reproduction
export XAI_API_KEY=xai-... # valid key
claudish --model grok-3 -p "hi"
Output:
[claudish] Model: grok-3
[Auto-route] Auto-routed: grok-3 -> xai (api-key)
[Auto-route] Auto-routed: grok-3 -> xai (api-key) # fires repeatedly (retry loop)
...
Probe confirms the failure:
$ claudish --probe grok-3
┌─ grok-3 ──────────────────────────────── xai · 0/1 live ─┐
│ 1 │ xAI │ xai@grok-3 │ ⊗ auth 401 │
│ └ Missing Authentication header │
Proxy log (~/.claudish/logs/...):
[Proxy] Server started on port 4690
[Auto-route] Auto-routed: grok-3 -> xai (api-key)
{"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"},"request_id":"req_011CamQ..."}
Evidence the bug is on claudish's side, not XAI's
-
Direct curl with the same key works:
curl https://api.x.ai/v1/chat/completions \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{"model":"grok-3","messages":[{"role":"user","content":"hi"}]}'
# → 200 OK, valid response
-
XAI's actual error vocabulary differs. When Authorization header is missing or empty, api.x.ai returns:
{"code":"...","error":"No credentials presented. [WKE=unauthenticated:no-credentials]"}
not "Missing Authentication header". The string "Missing Authentication header" does not appear in claudish's strings output and is not in XAI's error wording — it appears to be generated internally inside the bundled JS when key resolution returns empty.
-
Provider config in the binary is correct:
name: "xai"
transport: "openai"
baseUrl: "https://api.x.ai"
apiPath: "/v1/chat/completions"
apiKeyEnvVar: "XAI_API_KEY"
Default authScheme for the openai transport is "bearer" (also confirmed in strings).
-
Workarounds tried, all fail with the same error:
- Bare
--model grok-3 → routes to xai, fails
- Explicit
--model xai@grok-3 → routes to OpenRouter (wrong) → 401 Missing OPENROUTER_API_KEY
- Explicit
--model x-ai@grok-3 → same OpenRouter fallthrough
- Adding
apiKeys.XAI_API_KEY to ~/.claudish/config.json → no effect
- Defining XAI as a
customEndpoints entry with explicit apiKey field → still Missing Authentication header
Likely root cause
The OpenAIProviderTransport (or its key-resolution helper) is not reading process.env[provider.apiKeyEnvVar] at request time for the XAI provider. The auto-router can read XAI_API_KEY (the Auto-routed: ... -> xai (api-key) log line proves it sees the key for routing decisions), but the request-builder code path drops it before constructing the outbound Authorization header. Other providers using the same transport: "openai" (e.g. openai, glm) appear to work, so the bug may be specific to how XAI's provider entry is wired into the transport — possibly a missing authScheme: "bearer" field on the provider config (other openai-transport providers like openrouter declare it explicitly).
Suggested fix
Either (a) ensure the OpenAIProviderTransport always falls back to process.env[provider.apiKeyEnvVar] when the resolved key is empty, or (b) explicitly set authScheme: "bearer" on the XAI provider entry alongside the existing transport: "openai".
Impact
All XAI/Grok routing is currently non-functional in v7.0.3 with environment-variable auth, which is the documented setup path. Users hit a confusing error message that points at credentials when the credentials are correct.
Summary
On v7.0.3, every Grok model (
grok-3,grok-4,grok-4.3,grok-4-fast-*, etc.) fails withMissing Authentication headerfrom the proxy, despiteXAI_API_KEYbeing correctly set in the environment. Direct calls toapi.x.aiwith the same key succeed, so the key itself is valid — claudish is not threading it onto the outbound request.Environment
/opt/homebrew/bin/claudish)XAI_API_KEYset (84 chars, no whitespace, validated).ANTHROPIC_API_KEYunset (OAuth login via Claude Code).Reproduction
Output:
Probe confirms the failure:
Proxy log (
~/.claudish/logs/...):Evidence the bug is on claudish's side, not XAI's
Direct curl with the same key works:
XAI's actual error vocabulary differs. When
Authorizationheader is missing or empty, api.x.ai returns:{"code":"...","error":"No credentials presented. [WKE=unauthenticated:no-credentials]"}not
"Missing Authentication header". The string"Missing Authentication header"does not appear in claudish'sstringsoutput and is not in XAI's error wording — it appears to be generated internally inside the bundled JS when key resolution returns empty.Provider config in the binary is correct:
Default
authSchemefor theopenaitransport is"bearer"(also confirmed in strings).Workarounds tried, all fail with the same error:
--model grok-3→ routes to xai, fails--model xai@grok-3→ routes to OpenRouter (wrong) → 401 Missing OPENROUTER_API_KEY--model x-ai@grok-3→ same OpenRouter fallthroughapiKeys.XAI_API_KEYto~/.claudish/config.json→ no effectcustomEndpointsentry with explicitapiKeyfield → stillMissing Authentication headerLikely root cause
The
OpenAIProviderTransport(or its key-resolution helper) is not readingprocess.env[provider.apiKeyEnvVar]at request time for the XAI provider. The auto-router can readXAI_API_KEY(theAuto-routed: ... -> xai (api-key)log line proves it sees the key for routing decisions), but the request-builder code path drops it before constructing the outboundAuthorizationheader. Other providers using the sametransport: "openai"(e.g.openai,glm) appear to work, so the bug may be specific to how XAI's provider entry is wired into the transport — possibly a missingauthScheme: "bearer"field on the provider config (other openai-transport providers likeopenrouterdeclare it explicitly).Suggested fix
Either (a) ensure the
OpenAIProviderTransportalways falls back toprocess.env[provider.apiKeyEnvVar]when the resolved key is empty, or (b) explicitly setauthScheme: "bearer"on the XAI provider entry alongside the existingtransport: "openai".Impact
All XAI/Grok routing is currently non-functional in v7.0.3 with environment-variable auth, which is the documented setup path. Users hit a confusing error message that points at credentials when the credentials are correct.