charmbracelet/crush · error

failed to load shell config %s: %w

Error message

failed to load shell config %s: %w

What it means

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.

Source

Thrown at internal/config/load.go:996

		if path == "" {
			continue
		}
		data, err := os.ReadFile(path)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return nil, nil, fmt.Errorf("failed to open config file %s: %w", path, err)
		}
		if len(data) == 0 {
			continue
		}

		dir := filepath.Dir(path)
		if isShellConfig(path) {
			jsonBytes, err := shellconfig.LoadShellConfig(ctx, path, data)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to load shell config %s: %w", path, err)
			}
			if len(jsonBytes) > 0 {
				if !json.Valid(jsonBytes) {
					return nil, nil, fmt.Errorf("shell config %s produced invalid JSON", path)
				}
				addTopLevelKeys(shDirKeys, dir, jsonBytes)
				configs = append(configs, jsonBytes)
				loaded = append(loaded, path)
			}
		} else {
			if !json.Valid(data) {
				return nil, nil, fmt.Errorf("invalid JSON in config file %s", path)
			}
			addTopLevelKeys(jsonDirKeys, dir, data)
			configs = append(configs, data)
			loaded = append(loaded, path)
		}
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Run bash -n on your crushrc to catch syntax errors before starting
  2. Check the inner error after the colon — it names the failing line/command in the shell config
  3. Simplify the crushrc: comment out blocks and re-add until the failing one is found
  4. Verify builtin usage (provider, model, mcp, lsp, permissions, hook, options) matches the documented syntax

Example fix

# before (crushrc)
provider anthropic {
  api_key $ANTHROPIC_API_KEY
# missing closing brace
# after
provider anthropic {
  api_key $ANTHROPIC_API_KEY
}
Defensive patterns

Strategy: validation

Validate before calling

// syntax-check a crushrc before load
if out, err := exec.Command("bash", "-n", crushrcPath).CombinedOutput(); err != nil {
  return fmt.Errorf("crushrc syntax error: %s: %w", out, err)
}

Try / catch

cfg, err := config.Load(ctx, ...)
if err != nil && strings.Contains(err.Error(), "failed to load shell config") {
  // surface the inner bash error line to the user, do not retry blindly
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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