{"record":{"id":"2d31f4d995b33323","repo":"charmbracelet/crush","slug":"hook-s-d-invalid-matcher-regex-q-w","errorCode":null,"errorMessage":"hook %s[%d]: invalid matcher regex %q: %w","messagePattern":"hook (.+?)\\[(.+?)\\]: invalid matcher regex %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/load.go","lineNumber":1452,"sourceCode":"\t// Normalize event name keys.\n\tfor event, eventHooks := range c.Hooks {\n\t\tcanonical := normalizeHookEvent(event)\n\t\tif canonical != event {\n\t\t\tc.Hooks[canonical] = append(c.Hooks[canonical], eventHooks...)\n\t\t\tdelete(c.Hooks, event)\n\t\t}\n\t}\n\n\tfor event, eventHooks := range c.Hooks {\n\t\tfor i, h := range eventHooks {\n\t\t\tif h.Command == \"\" {\n\t\t\t\treturn fmt.Errorf(\"hook %s[%d]: command is required\", event, i)\n\t\t\t}\n\t\t\tif h.Matcher == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, err := regexp.Compile(h.Matcher); err != nil {\n\t\t\t\treturn fmt.Errorf(\"hook %s[%d]: invalid matcher regex %q: %w\", event, i, h.Matcher, err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":1434,"sourceCodeEnd":1458,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/load.go#L1434-L1458","documentation":"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.","triggerScenarios":"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\\(\".","commonSituations":"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.","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."],"exampleFix":"// before (crush.json)\n{\"hooks\": {\"PreToolUse\": [{\"matcher\": \"Bash(git *)\", \"command\": \"lint.sh\"}]}}\n// after\n{\"hooks\": {\"PreToolUse\": [{\"matcher\": \"^Bash$\", \"command\": \"lint.sh\"}]}}","handlingStrategy":"validation","validationCode":"for event, hooks := range cfg.Hooks {\n    for i, h := range hooks {\n        if h.Matcher == \"\" { continue }\n        if _, err := regexp.Compile(h.Matcher); err != nil {\n            return fmt.Errorf(\"hook %s[%d]: invalid matcher regex %q: %w\", event, i, h.Matcher, err)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.ValidateHooks(); err != nil {\n    var re *regexp.SyntaxError\n    if errors.As(err, &re) {\n        slog.Error(\"fix hook matcher regex\", \"detail\", re.String())\n    }\n    return err\n}","preventionTips":["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 \\()."],"tags":["config","regex","hooks","validation"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}