Kuberwastaken/claurst · error · anyhow::Error
hooks. hook is missing command
Error message
hooks.{event_name} hook is missing command What it means
In an imported hooks config, a command hook object under hooks.<event>[].hooks[] lacks the `command` field. The importer requires every hook entry to carry an explicit command string; hooks defined only with type or other keys are rejected.
Solutions
- Add "command": "<shell command>" (string) to every hook object under hooks.[].hooks[]
- Rename tool-specific keys (cmd, run, script) to "command"
- Ensure the command value is a plain quoted string, not a number, array, or object
Example fix
// before
{ "hooks": [ { "type": "command" } ] }
// after
{ "hooks": [ { "command": "./scripts/notify.sh" } ] } Defensive patterns
Strategy: validation
Validate before calling
for (const groups of Object.values(cfg.hooks ?? {})) {
for (const g of groups) for (const h of g.hooks ?? []) if (typeof h?.command !== 'string') throw new Error('every hook needs a string "command"');
} Type guard
const hasCommand = (h) => typeof h === 'object' && h !== null && typeof h.command === 'string' && h.command.length > 0;
Try / catch
try { importConfig(path) } catch (e) { if (String(e).includes('hook is missing command')) { console.error('Add a string "command" field to the named event hook'); process.exitCode = 1; } else { throw e; } } Prevention
- Each hook object must contain a non-empty string "command"
- Rename cmd/run/script keys to "command" when migrating configs
- Ensure the command is quoted as a JSON string, not a number or array
When it happens
Trigger: Importing a hook object like {"type": "command"} without the command value, {"command": 42}, or a hook with only "url"/"prompt" style fields from a different tool's schema.
Common situations: Migrating hooks from a tool whose hook objects use different key names, truncation during copy-paste, or quoting errors that made the command value a nested structure.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- mcpServers. is missing command/url
- hooks must be an object
- hooks. must be an array
- hooks. [] must be an object
- hooks. [].hooks must be an array
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/82f6786dda8f4c90.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/core/src/import_config.rs:737
.as_object()
.ok_or_else(|| anyhow!("hooks.{event_name}[] must be an object"))?;
let matcher = entry_obj
.get("matcher")
.and_then(Value::as_str)
.unwrap_or("*")
.to_string();
let hooks = entry_obj
.get("hooks")
.and_then(Value::as_array)
.ok_or_else(|| anyhow!("hooks.{event_name}[].hooks must be an array"))?;
for hook in hooks {
let hook_obj = hook
.as_object()
.ok_or_else(|| anyhow!("hooks.{event_name}[].hooks[] must be an object"))?;
let command = hook_obj
.get("command")
.and_then(Value::as_str)
.ok_or_else(|| anyhow!("hooks.{event_name} hook is missing command"))?
.to_string();
hook_entries.push(HookEntry {
command,
tool_filter: if matcher == "*" { None } else { Some(matcher.clone()) },
blocking: false,
});
}
}
out.insert(event, hook_entries);
}
Ok(out)
}
fn parse_hook_event(name: &str) -> Result<HookEvent> {
match name {
"PreToolUse" => Ok(HookEvent::PreToolUse),
"PostToolUse" => Ok(HookEvent::PostToolUse),
"Stop" => Ok(HookEvent::Stop),View on GitHub (pinned to b0637c97ec)