{"record":{"id":"1dabce879d29c02e","repo":"charmbracelet/crush","slug":"env-s-w","errorCode":null,"errorMessage":"env %s: %w","messagePattern":"env (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/config.go","lineNumber":1066,"sourceCode":"// resolveEnvs expands every value in envs through the given resolver\n// and returns a fresh \"KEY=value\" slice sorted by key. The input map is\n// not mutated. On the first resolution failure it returns nil and an\n// error identifying the offending variable; the inner resolver error is\n// already sanitized by ResolveValue and is wrapped with %w.\nfunc resolveEnvs(envs map[string]string, r VariableResolver) ([]string, error) {\n\tif len(envs) == 0 {\n\t\treturn nil, nil\n\t}\n\tkeys := make([]string, 0, len(envs))\n\tfor k := range envs {\n\t\tkeys = append(keys, k)\n\t}\n\tslices.Sort(keys)\n\tres := make([]string, 0, len(envs))\n\tfor _, k := range keys {\n\t\tv, err := r.ResolveValue(envs[k])\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"env %s: %w\", k, err)\n\t\t}\n\t\tres = append(res, fmt.Sprintf(\"%s=%s\", k, v))\n\t}\n\treturn res, nil\n}\n\nfunc ptrValOr[T any](t *T, el T) T {\n\tif t == nil {\n\t\treturn el\n\t}\n\treturn *t\n}\n","sourceCodeStart":1048,"sourceCodeEnd":1079,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/config.go#L1048-L1079","documentation":"Wraps a resolver failure while expanding the string-slice form of env settings (used for MCP servers, LSPs, etc.) in internal/config/config.go. Each entry is resolved and then joined as KEY=value; if resolving any entry fails, the error names the offending key. Like error 500, it signals an unset variable or bad env-reference syntax.","triggerScenarios":"Config resolution of an MCP/LSP entry whose env list contains a value referencing an environment variable that cannot be resolved — unset variable or malformed {{ env:... }} reference.","commonSituations":"An MCP server config referencing a token env var that is only set in a different shell; LSP env entries copied from docs with placeholder variable names; missing .env file that a team relies on.","solutions":["Export the named variable (from the error message) before launching Crush.","Correct the reference syntax or variable name in the MCP/LSP env entry.","Add the variable to a shell profile, direnv .envrc, or CI secret store so it is always present.","Remove the env entry if the tool does not actually require it."],"exampleFix":"// before (MCP config)\nmcp github\n  env GITHUB_TOKEN \"{{ env:GITHUB_TKN }}\" // typo\nend\n// after\nexport GITHUB_TOKEN=ghp_...\nmcp github\n  env GITHUB_TOKEN \"{{ env:GITHUB_TOKEN }}\"\nend","handlingStrategy":"validation","validationCode":"for _, entry := range mcpCfg.Env {\n    k, v, _ := strings.Cut(entry, \"=\")\n    if err := resolver.ResolveValue(v); err != nil {\n        return fmt.Errorf(\"MCP %q requires env %s which is unresolvable\", mcpCfg.Name, k)\n    }\n}","typeGuard":"func allEnvKeysSet(envs []string, lookup func(string) bool) bool {\n    for _, e := range envs {\n        if k, v, ok := strings.Cut(e, \"=\"); ok && strings.Contains(v, \"env:\") && !lookup(k) {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"if err := cfg.ResolveMCPEnvs(); err != nil {\n    if strings.Contains(err.Error(), \"env \") {\n        log.Fatalf(\"export the missing variable listed in the error, then restart: %v\", err)\n    }\n    return err\n}","preventionTips":["Keep MCP/LSP env vars in direnv or a startup script so every shell has them.","Maintain a .env.example listing all referenced variable names.","Grep crushrc for {{ env: references and assert each exists in CI."],"tags":["config","environment-variables","mcp","lsp"],"backgroundTag":"missing-env-var","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}