charmbracelet/crush · error

shell config %s produced invalid JSON

Error message

shell config %s produced invalid JSON

What it means

After shellconfig.LoadShellConfig succeeds it must yield valid JSON. If the produced byte slice is non-empty but json.Valid reports invalid, loadFromConfigPaths aborts with this error. This guards against a crushrc that evaluates without error but emits malformed JSON.

Source

Thrown at internal/config/load.go:1000

		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)
		}
	}

	// Warn if both a JSON config and a crushrc exist in the same directory
	// and define overlapping top-level keys. Disjoint coexistence is
	// intentional and not worth warning about.

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Validate the JSON your crushrc should produce by inspecting the inner generated output or running the builtins manually
  2. Remove or fix the builtin/option emitting the malformed output
  3. Check for BOM or non-UTF8 characters in the crushrc and re-save as UTF-8
  4. Update Crush — builtin emission bugs are fixed in newer releases

Example fix

# before: duplicate provider blocks emit conflicting JSON keys
provider a { ... }
provider a { ... }
# after: declare each provider once
provider a { ... }
Defensive patterns

Strategy: validation

Validate before calling

// after producing JSON from a custom builtin pipeline:
if !json.Valid(jsonBytes) {
  return fmt.Errorf("shell config produced invalid JSON")
}

Try / catch

if err := config.Load(ctx, ...); err != nil {
  if strings.Contains(err.Error(), "produced invalid JSON") {
    // disable custom builtins and bisect which one emits bad output
  }
}

Prevention

When it happens

Trigger: Load processes a crushrc where LoadShellConfig returns bytes that are non-empty and fail json.Valid — e.g. duplicated keys from bad merging, truncated output, or a builtin emitting non-JSON text into the config.

Common situations: Custom or outdated shell builtins writing raw text; encoding issues (BOM, non-UTF8 bytes) in generated JSON; a bug in a custom builtin that produces near-JSON output.

Understand the failure class

Related errors


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