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.Models

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Set the env var referenced by the failing header before starting crush
  2. Fix the header value in config (correct var name or working command)
  3. Remove the header if it is not needed for that provider
  4. 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

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


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/ab7e114b49fa3def. Report an issue: GitHub.