charmbracelet/crush · error
failed to connect to provider %s: %s
Error message
failed to connect to provider %s: %s
What it means
Returned when the Z.AI provider probe responds with HTTP 401 Unauthorized during API-key validation. For ZAI, a 401 is treated as a definitive key failure (other statuses are tolerated as inconclusive), so the response status text is surfaced to the user.
Source
Thrown at internal/config/config.go:1038
return fmt.Errorf("failed to create request for provider %s: %w", c.ID, err)
}
for k, v := range headers {
req.Header.Set(k, v)
}
for k, v := range c.ExtraHeaders {
req.Header.Set(k, v)
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to create request for provider %s: %w", c.ID, err)
}
defer resp.Body.Close()
switch providerID {
case catwalk.InferenceProviderZAI:
if resp.StatusCode == http.StatusUnauthorized {
return fmt.Errorf("failed to connect to provider %s: %s", c.ID, resp.Status)
}
default:
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to connect to provider %s: %s", c.ID, resp.Status)
}
}
return nil
}
// resolveEnvs expands every value in envs through the given resolver
// and returns a fresh "KEY=value" slice sorted by key. The input map is
// not mutated. On the first resolution failure it returns nil and an
// error identifying the offending variable; the inner resolver error is
// already sanitized by ResolveValue and is wrapped with %w.
func resolveEnvs(envs map[string]string, r VariableResolver) ([]string, error) {
if len(envs) == 0 {
return nil, nil
}View on GitHub (pinned to 7944b8e522)
Solutions
- Generate a fresh API key from the Z.AI console and update it in crushrc/crush.json.
- Confirm the key matches the endpoint region you configured (api.z.ai vs open.bigmodel.cn).
- Verify the env variable holding the key is actually set and non-empty for the process.
- Test the key directly: curl -H "Authorization: Bearer $KEY" https://api.z.ai/... to see the same 401.
Example fix
// before (crushrc)
provider zai
api_key "{{ env:OLD_REVOKED_KEY }}"
end
// after
export ZAI_API_KEY=<new key from Z.AI console>
provider zai
api_key "{{ env:ZAI_API_KEY }}"
end Defensive patterns
Strategy: validation
Validate before calling
if zaiKey == "" {
return errors.New("ZAI_API_KEY is not set; obtain one from the Z.AI console before validating")
}
// optional live pre-check:
resp, err := http.Get("https://api.z.ai/v1/models") // expect non-401 Type guard
func isAuthFailure(statusCode int) bool { return statusCode == http.StatusUnauthorized || statusCode == http.StatusForbidden } Try / catch
if err := validateProviderKey(ctx, zaiCfg); err != nil {
if strings.Contains(err.Error(), "401") {
openKeyConsole("https://z.ai") // guide user to rotate key
return nil
}
return err
} Prevention
- Rotate keys on a schedule and update config immediately after revocation.
- Keep separate keys for international vs China Z.AI endpoints.
- Verify the env var is exported in the environment where Crush runs.
When it happens
Trigger: Calling API-key validation for InferenceProviderZAI and receiving a 401 response — the configured Z.AI API key is missing, expired, revoked, or malformed.
Common situations: Rotated or deleted Z.AI key still referenced in config; key set for the wrong Z.AI endpoint (international vs China); Bearer header not being sent because the key resolved to an empty string.
Related errors
- not a valid bedrock api key
- not a valid vercel api key
- unsupported api key type %T
- failed to set provider API key: status code %d
- failed to import copilot: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/fa60084e8d9755c9.
Report an issue: GitHub.