charmbracelet/crush · error
resolving provider %s header %q: %w
Error message
resolving provider %s header %q: %w
What it means
While configuring a known (catalog) provider, each configured header value is resolved through the value resolver (env vars, commands). If resolution fails (e.g. strict unset variable or failing command substitution), configureProviders returns this error naming the provider ID and header key.
Source
Thrown at internal/config/load.go:281
}
}
headers := map[string]string{}
if len(p.DefaultHeaders) > 0 {
maps.Copy(headers, p.DefaultHeaders)
}
if len(config.ExtraHeaders) > 0 {
maps.Copy(headers, config.ExtraHeaders)
}
// Provider headers use the same error contract as MCP headers:
// a failing $(...) aborts the provider load with a clear
// message, and a header that resolves to the empty string
// (unset bare $VAR under lenient nounset, $(echo), or literal
// "") is dropped from the outgoing request.
for k, v := range headers {
resolved, err := resolver.ResolveValue(v)
if err != nil {
return fmt.Errorf("resolving provider %s header %q: %w", p.ID, k, err)
}
if resolved == "" {
delete(headers, k)
continue
}
headers[k] = resolved
}
// Start from user config so all user fields survive without
// explicit copying. Overlay catwalk identity/endpoint fields
// (already merged with user overrides above).
prepared := config
prepared.ID = string(p.ID)
prepared.Name = p.Name
prepared.BaseURL = p.APIEndpoint
prepared.APIKey = p.APIKey
prepared.APIKeyTemplate = p.APIKey // Store original template for re-resolution
prepared.Type = p.Type
prepared.Models = p.ModelsView on GitHub (pinned to 7944b8e522)
Solutions
- Set the env var referenced by the failing header before starting crush
- Fix the header value in config (correct var name or working command)
- Remove the header if it is not needed for that provider
- Use lenient resolution (if supported) or a literal value instead of strict $VAR resolution
Example fix
// before (crush.json)
"headers": {"X-Api-Key": "$ANTHROPIC_KEY"} // var unset
// after
$ export ANTHROPIC_KEY=sk-...
// or in config:
"headers": {"x-api-key": "sk-..."} Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: ensure every $VAR referenced in provider headers is set
re := regexp.MustCompile(`\$([A-Z_][A-Z0-9_]*)`)
for _, v := range providerHeaders {
for _, m := range re.FindAllStringSubmatch(v, -1) {
if os.Getenv(m[1]) == "" {
return fmt.Errorf("env var %s used in provider header is unset", m[1])
}
}
} Try / catch
store, err := config.Load(ctx, opts)
if err != nil {
var msg string
if fmt.Sprint(err) != "" && strings.Contains(err.Error(), "resolving provider ") && strings.Contains(err.Error(), "header") {
return fmt.Errorf("set the env var named in the error: %w", err)
}
_ = msg
return err
} Prevention
- Export all header env vars in shell profile or a sourced .env
- Use options.env in crush config to define header secrets declaratively
- Avoid $(command) substitutions in headers for non-interactive environments
- Double-check env var spelling against the error message
When it happens
Trigger: resolver.ResolveValue(v) errors for a header of a known provider — e.g. header "Authorization: Bearer $MY_KEY" where MY_KEY is unset under strict resolution, or a $(cmd) substitution exits non-zero.
Common situations: Missing API-key env var referenced in provider headers; shell command in header value failing; typo in env var name; running in environment where the var was set in a different shell session.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- header %s: %w
- no providers found
- no providers found matching %q
- no providers configured - please run 'crush' to set up a pro
- invalid source %q, must be 'catwalk' or 'hyper'
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/ab7e114b49fa3def.
Report an issue: GitHub.