{"record":{"id":"ab7e114b49fa3def","repo":"charmbracelet/crush","slug":"resolving-provider-s-header-q-w","errorCode":null,"errorMessage":"resolving provider %s header %q: %w","messagePattern":"resolving provider (.+?) header %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/load.go","lineNumber":281,"sourceCode":"\t\t\t}\n\t\t}\n\n\t\theaders := map[string]string{}\n\t\tif len(p.DefaultHeaders) > 0 {\n\t\t\tmaps.Copy(headers, p.DefaultHeaders)\n\t\t}\n\t\tif len(config.ExtraHeaders) > 0 {\n\t\t\tmaps.Copy(headers, config.ExtraHeaders)\n\t\t}\n\t\t// Provider headers use the same error contract as MCP headers:\n\t\t// a failing $(...) aborts the provider load with a clear\n\t\t// message, and a header that resolves to the empty string\n\t\t// (unset bare $VAR under lenient nounset, $(echo), or literal\n\t\t// \"\") is dropped from the outgoing request.\n\t\tfor k, v := range headers {\n\t\t\tresolved, err := resolver.ResolveValue(v)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"resolving provider %s header %q: %w\", p.ID, k, err)\n\t\t\t}\n\t\t\tif resolved == \"\" {\n\t\t\t\tdelete(headers, k)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\theaders[k] = resolved\n\t\t}\n\t\t// Start from user config so all user fields survive without\n\t\t// explicit copying. Overlay catwalk identity/endpoint fields\n\t\t// (already merged with user overrides above).\n\t\tprepared := config\n\t\tprepared.ID = string(p.ID)\n\t\tprepared.Name = p.Name\n\t\tprepared.BaseURL = p.APIEndpoint\n\t\tprepared.APIKey = p.APIKey\n\t\tprepared.APIKeyTemplate = p.APIKey // Store original template for re-resolution\n\t\tprepared.Type = p.Type\n\t\tprepared.Models = p.Models","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/load.go#L263-L299","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (crush.json)\n\"headers\": {\"X-Api-Key\": \"$ANTHROPIC_KEY\"}  // var unset\n// after\n$ export ANTHROPIC_KEY=sk-...\n// or in config:\n\"headers\": {\"x-api-key\": \"sk-...\"}","handlingStrategy":"validation","validationCode":"// pre-flight: ensure every $VAR referenced in provider headers is set\nre := regexp.MustCompile(`\\$([A-Z_][A-Z0-9_]*)`)\nfor _, v := range providerHeaders {\n    for _, m := range re.FindAllStringSubmatch(v, -1) {\n        if os.Getenv(m[1]) == \"\" {\n            return fmt.Errorf(\"env var %s used in provider header is unset\", m[1])\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"store, err := config.Load(ctx, opts)\nif err != nil {\n    var msg string\n    if fmt.Sprint(err) != \"\" && strings.Contains(err.Error(), \"resolving provider \") && strings.Contains(err.Error(), \"header\") {\n        return fmt.Errorf(\"set the env var named in the error: %w\", err)\n    }\n    _ = msg\n    return err\n}","preventionTips":["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"],"tags":["config","providers","headers","env"],"backgroundTag":"missing-env-var","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}