charmbracelet/crush · error
hook %s[%d]: command is required
Error message
hook %s[%d]: command is required
What it means
ValidateHooks iterates c.Hooks per event and index. Every hook must have a non-empty Command; a hook entry that only sets a matcher (or is otherwise empty) fails validation with 'hook <event>[<i>]: command is required'. This runs during Load and reloadFromDiskLocked before hooks are registered with the runner.
Source
Thrown at internal/config/load.go:1446
// ValidateHooks normalizes event names and checks that every configured
// hook has a command and a syntactically valid matcher regex. Matcher
// compilation used for matching is owned by hooks.Runner; this function
// only validates up front so the user sees config errors at load time
// rather than on the first tool call.
func (c *Config) ValidateHooks() error {
// 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
- Set the command field for the hook named by <event>[<i>] in the error message
- Check for key typos ('cmd' vs 'command') that leave Command empty
- If the shell variable holding the command is empty at eval time, guard it or give a default
- Remove the hook entry entirely if it is no longer needed
Example fix
# before (crushrc)
hook pre_tool_use {
matcher "bash"
}
# after
hook pre_tool_use {
matcher "bash"
command "echo 'bash used'"
} Defensive patterns
Strategy: validation
Validate before calling
for event, hooks := range cfg.Hooks {
for i, h := range hooks {
if h.Command == "" {
return fmt.Errorf("hook %s[%d] missing command", event, i)
}
}
} Try / catch
if err := config.Load(ctx, ...); err != nil {
var msg string
if errors.As(err, new(error)) && strings.Contains(err.Error(), "command is required") {
// point the user at the exact event[index] named in the message
}
_ = msg
} Prevention
- Always set both matcher and command together in hook blocks
- Use the exact key 'command', not 'cmd'
- Guard shell variables used as commands with defaults: ${CMD:-echo}
When it happens
Trigger: Load/ValidateHooks encounters a hook in c.Hooks (from the hook builtin in crushrc or the hooks JSON block) whose Command field is the empty string — e.g. hook defined with only a matcher, type, or timeout.
Common situations: Copy-pasted hook block with the command line removed or commented out; YAML/JSON key typo like 'cmd' instead of 'command' so Command stays empty; a hook shell variable that expanded to nothing in crushrc.
Related errors
- invalid hook configuration: %w
- hook %s[%d]: invalid matcher regex %q: %w
- not a valid bedrock api key
- not a valid vercel api key
- mcp stdio config requires a non-empty 'command' field
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/35d8649a4a765866.
Report an issue: GitHub.