{"record":{"id":"98f84dd3c36843e8","repo":"charmbracelet/crush","slug":"failed-to-load-shell-config-s-w","errorCode":null,"errorMessage":"failed to load shell config %s: %w","messagePattern":"failed to load shell config (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/load.go","lineNumber":996,"sourceCode":"\t\tif path == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tdata, err := os.ReadFile(path)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to open config file %s: %w\", path, err)\n\t\t}\n\t\tif len(data) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tdir := filepath.Dir(path)\n\t\tif isShellConfig(path) {\n\t\t\tjsonBytes, err := shellconfig.LoadShellConfig(ctx, path, data)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to load shell config %s: %w\", path, err)\n\t\t\t}\n\t\t\tif len(jsonBytes) > 0 {\n\t\t\t\tif !json.Valid(jsonBytes) {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"shell config %s produced invalid JSON\", path)\n\t\t\t\t}\n\t\t\t\taddTopLevelKeys(shDirKeys, dir, jsonBytes)\n\t\t\t\tconfigs = append(configs, jsonBytes)\n\t\t\t\tloaded = append(loaded, path)\n\t\t\t}\n\t\t} else {\n\t\t\tif !json.Valid(data) {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"invalid JSON in config file %s\", path)\n\t\t\t}\n\t\t\taddTopLevelKeys(jsonDirKeys, dir, data)\n\t\t\tconfigs = append(configs, data)\n\t\t\tloaded = append(loaded, path)\n\t\t}\n\t}","sourceCodeStart":978,"sourceCodeEnd":1014,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/load.go#L978-L1014","documentation":"When a candidate config path is a shell config (crushrc), loadFromConfigPaths runs it through shellconfig.LoadShellConfig to produce JSON. Any error executing/evaluating the bash-based config is wrapped with this message and aborts config loading.","triggerScenarios":"Load reads a crushrc file, isShellConfig(path) is true, and shellconfig.LoadShellConfig fails: bash syntax error, unknown builtin, failing command substitution, or the ConfigBuilder gate rejects a builtin call.","commonSituations":"Bash typo or unbalanced quotes in crushrc; referencing an undefined variable at config-eval time; a builtin like provider/mcp called with wrong arguments; running in an environment where the embedded bash cannot execute.","solutions":["Run bash -n on your crushrc to catch syntax errors before starting","Check the inner error after the colon — it names the failing line/command in the shell config","Simplify the crushrc: comment out blocks and re-add until the failing one is found","Verify builtin usage (provider, model, mcp, lsp, permissions, hook, options) matches the documented syntax"],"exampleFix":"# before (crushrc)\nprovider anthropic {\n  api_key $ANTHROPIC_API_KEY\n# missing closing brace\n# after\nprovider anthropic {\n  api_key $ANTHROPIC_API_KEY\n}","handlingStrategy":"validation","validationCode":"// syntax-check a crushrc before load\nif out, err := exec.Command(\"bash\", \"-n\", crushrcPath).CombinedOutput(); err != nil {\n  return fmt.Errorf(\"crushrc syntax error: %s: %w\", out, err)\n}","typeGuard":null,"tryCatchPattern":"cfg, err := config.Load(ctx, ...)\nif err != nil && strings.Contains(err.Error(), \"failed to load shell config\") {\n  // surface the inner bash error line to the user, do not retry blindly\n}","preventionTips":["Run bash -n on crushrc in CI","Quote all variable expansions in crushrc","Keep builtin calls to documented builtins only"],"tags":["config","bash","shellconfig"],"backgroundTag":"shell-config-load-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}