charmbracelet/crush · error
hook %s[%d]: invalid matcher regex %q: %w
Error message
hook %s[%d]: invalid matcher regex %q: %w
What it means
ValidateHooks checks every hook in the config, requiring a non-empty command and a syntactically valid matcher regex. This error is returned when a hook's matcher string is non-empty but regexp.Compile rejects it, wrapping the underlying regexp parse error (unclosed group, bad escape, etc.). Crush validates hooks at load and reload time so a bad matcher never silently matches all tools or crashes the hook runner.
Source
Thrown at internal/config/load.go:1452
// Normalize event name keys.
for event, eventHooks := range c.Hooks {
canonical := normalizeHookEvent(event)
if canonical != event {
c.Hooks[canonical] = append(c.Hooks[canonical], eventHooks...)
delete(c.Hooks, event)
}
}
for event, eventHooks := range c.Hooks {
for i, h := range eventHooks {
if h.Command == "" {
return fmt.Errorf("hook %s[%d]: command is required", event, i)
}
if h.Matcher == "" {
continue
}
if _, err := regexp.Compile(h.Matcher); err != nil {
return fmt.Errorf("hook %s[%d]: invalid matcher regex %q: %w", event, i, h.Matcher, err)
}
}
}
return nil
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Test the pattern with `go run` / regex101 in Go mode and fix the syntax error reported after the colon in the wrapped message.
- Remove the matcher field entirely (empty string means match all tools and is allowed).
- Escape regex metacharacters: use `\\(` for a literal paren, `\\.` for a literal dot.
- Re-run `crush` or reload config to confirm ValidateHooks passes.
Example fix
// before (crush.json)
{"hooks": {"PreToolUse": [{"matcher": "Bash(git *)", "command": "lint.sh"}]}}
// after
{"hooks": {"PreToolUse": [{"matcher": "^Bash$", "command": "lint.sh"}]}} Defensive patterns
Strategy: validation
Validate before calling
for event, hooks := range cfg.Hooks {
for i, h := range hooks {
if h.Matcher == "" { continue }
if _, err := regexp.Compile(h.Matcher); err != nil {
return fmt.Errorf("hook %s[%d]: invalid matcher regex %q: %w", event, i, h.Matcher, err)
}
}
} Try / catch
if err := cfg.ValidateHooks(); err != nil {
var re *regexp.SyntaxError
if errors.As(err, &re) {
slog.Error("fix hook matcher regex", "detail", re.String())
}
return err
} Prevention
- Test every matcher in Go regex mode (regex101) before committing config.
- Prefer anchored simple patterns like ^Bash$ over complex alternations.
- Remember empty matcher means match-all; omit the field when unnecessary.
- Double-escape backslashes in JSON strings (\\( not \().
When it happens
Trigger: Calling config.Load, ReloadFromDisk (reloadFromDiskLocked), ValidateHooks directly, or newRunner with a config whose HookConfig.Matcher is a non-empty invalid regex, e.g. "bash(", "^bash$[", or "tool\(".
Common situations: Hand-editing crushrc or crush.json hooks and typosing the pattern; copying Claude Code hook matchers that use glob-ish syntax ("Bash(* git *)") not valid Go regexp; escaping mistakes when writing backslashes in JSON.
Related errors
- invalid hook configuration: %w
- hook %s[%d]: command is required
- invalid hook configuration on reload: %w
- not a valid bedrock api key
- not a valid vercel api key
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/2d31f4d995b33323.
Report an issue: GitHub.