Kuberwastaken/claurst · error · anyhow::Error
hooks. [] must be an object
Error message
hooks.{event_name}[] must be an object What it means
In parse_hooks, every element of an event's array must be a hook-group JSON object with optional "matcher" and required "hooks". A non-object element (string, null, number) in the array raises this error naming the event.
Solutions
- Convert each array element into an object with a "hooks" array of {"command": ...} entries
- Remove non-object elements from the event array
- Keep the documented shape: event -> [ { matcher, hooks: [ { command } ] } ]
Example fix
// before
{ "hooks": { "PreToolUse": ["npx lint"] } }
// after
{ "hooks": { "PreToolUse": [ { "matcher": "*", "hooks": [ { "command": "npx lint" } ] } ] } } Defensive patterns
Strategy: type-guard
Validate before calling
for (const groups of Object.values(cfg.hooks ?? {})) {
for (const g of groups) if (typeof g !== 'object' || g === null || Array.isArray(g)) throw new Error('hook group entries must be objects');
} Type guard
const isHookGroup = (g) => typeof g === 'object' && g !== null && !Array.isArray(g) && Array.isArray(g.hooks);
Try / catch
try { importConfig(path) } catch (e) { if (/hooks\.\S+\[\] must be an object/.test(String(e))) { console.error('Wrap each hook entry in an object with matcher/hooks'); } else { throw e; } } Prevention
- Never place raw command strings directly in the event array
- Every event array element must be an object with a hooks array
- Lint the settings file for hook shape after manual edits
When it happens
Trigger: Importing config where the event array contains raw strings or nulls, e.g. "hooks": {"PreToolUse": ["npx lint"]} or [null].
Common situations: Copy-pasted hooks where entries lost their object wrapping, JSON edited by scripts that pushed plain strings, or an intentionally disabled hook left as null.
Related errors
- hooks must be an object
- hooks. must be an array
- hooks. [].hooks must be an array
- hooks. [].hooks[] must be an object
- mcpServers must be an object
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/1400b6af94b29b7c.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/core/src/import_config.rs:720
Ok(servers)
}
fn parse_hooks(value: &Value) -> Result<HashMap<HookEvent, Vec<HookEntry>>> {
let Some(obj) = value.as_object() else {
return Err(anyhow!("hooks must be an object"));
};
let mut out = HashMap::new();
for (event_name, event_value) in obj {
let event = parse_hook_event(event_name)?;
let entries = event_value
.as_array()
.ok_or_else(|| anyhow!("hooks.{event_name} must be an array"))?;
let mut hook_entries = Vec::new();
for entry in entries {
let entry_obj = entry
.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();View on GitHub (pinned to b0637c97ec)