charmbracelet/crush · error

invalid hook configuration: %w

Error message

invalid hook configuration: %w

What it means

After all config merging completes, Load() calls cfg.ValidateHooks() to compile hook matcher regexes and check hook definitions. Any hook-validation failure is wrapped with this message so the user knows the problem is in the hooks section of their config.

Source

Thrown at internal/config/load.go:85

	if wsData, err := os.ReadFile(store.workspacePath); err == nil && len(wsData) > 0 {
		if !json.Valid(wsData) {
			return nil, fmt.Errorf("invalid JSON in config file %s", store.workspacePath)
		}
		merged, mergeErr := loadFromBytes(append([][]byte{mustMarshalConfig(cfg)}, wsData))
		if mergeErr == nil {
			// Preserve defaults that setDefaults already applied.
			dataDir := cfg.Options.DataDirectory
			*cfg = *merged
			cfg.setDefaults(workingDir, dataDir)
			store.config = cfg
			store.loadedPaths = append(store.loadedPaths, store.workspacePath)
		}
	}

	// Validate hooks after all config merging is complete so workspace
	// hooks also get their matcher regexes compiled.
	if err := cfg.ValidateHooks(); err != nil {
		return nil, fmt.Errorf("invalid hook configuration: %w", err)
	}

	if !isInsideWorktree() {
		const depth = 2
		const items = 100
		slog.Warn("No git repository detected in working directory, will limit file walk operations", "depth", depth, "items", items)
		assignIfNil(&cfg.Tools.Ls.MaxDepth, depth)
		assignIfNil(&cfg.Tools.Ls.MaxItems, items)
		assignIfNil(&cfg.Options.TUI.Completions.MaxDepth, depth)
		assignIfNil(&cfg.Options.TUI.Completions.MaxItems, items)
	}

	if isAppleTerminal() {
		slog.Warn("Detected Apple Terminal, enabling transparent mode")
		assignIfNil(&cfg.Options.TUI.Transparent, true)
	}

	// Load known providers, this loads the config from catwalk. A failed

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Read the wrapped %w error to identify which hook failed and fix its definition
  2. Check that every hook's matcher is a valid regular expression
  3. Verify hook event names match supported events (see HOOKS.md)
  4. Temporarily remove hooks from workspace config to isolate whether the global or workspace hook is at fault

Example fix

// before (crush.json)
"hooks": {"pre_tool_use": [{"matcher": "edit(", "command": "lint.sh"}]}
// after
"hooks": {"pre_tool_use": [{"matcher": "edit", "command": "lint.sh"}]}
Defensive patterns

Strategy: validation

Validate before calling

cfg, err := loadRawConfig()
if err != nil { return err }
if err := cfg.ValidateHooks(); err != nil {
    return fmt.Errorf("hooks invalid before startup: %w", err)
}

Try / catch

store, err := config.Load(ctx, opts)
if err != nil {
    if strings.HasPrefix(err.Error(), "invalid hook configuration:") {
        log.Fatalf("Fix hooks in crush config: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Load() finishes merging global and workspace configs; ValidateHooks() returns an error such as an invalid matcher regex or malformed hook command/event definition defined in crushrc/crush.json.

Common situations: Typo in a hook event name; a preToolUse matcher regex that doesn't compile (e.g. unbalanced parenthesis '['); hook defined in workspace config overriding/breaking a valid global hook.

Related errors


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