{"record":{"id":"5a001e7767ac7438","repo":"Kuberwastaken/claurst","slug":"hooks-event-name-hooks-must-be-an-array","errorCode":null,"errorMessage":"hooks.{event_name}[].hooks must be an array","messagePattern":"hooks\\.(.+?)\\[\\]\\.hooks must be an array","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-rust/crates/core/src/import_config.rs","lineNumber":729,"sourceCode":"    for (event_name, event_value) in obj {\n        let event = parse_hook_event(event_name)?;\n        let entries = event_value\n            .as_array()\n            .ok_or_else(|| anyhow!(\"hooks.{event_name} must be an array\"))?;\n        let mut hook_entries = Vec::new();\n        for entry in entries {\n            let entry_obj = entry\n                .as_object()\n                .ok_or_else(|| anyhow!(\"hooks.{event_name}[] must be an object\"))?;\n            let matcher = entry_obj\n                .get(\"matcher\")\n                .and_then(Value::as_str)\n                .unwrap_or(\"*\")\n                .to_string();\n            let hooks = entry_obj\n                .get(\"hooks\")\n                .and_then(Value::as_array)\n                .ok_or_else(|| anyhow!(\"hooks.{event_name}[].hooks must be an array\"))?;\n            for hook in hooks {\n                let hook_obj = hook\n                    .as_object()\n                    .ok_or_else(|| anyhow!(\"hooks.{event_name}[].hooks[] must be an object\"))?;\n                let command = hook_obj\n                    .get(\"command\")\n                    .and_then(Value::as_str)\n                    .ok_or_else(|| anyhow!(\"hooks.{event_name} hook is missing command\"))?\n                    .to_string();\n                hook_entries.push(HookEntry {\n                    command,\n                    tool_filter: if matcher == \"*\" { None } else { Some(matcher.clone()) },\n                    blocking: false,\n                });\n            }\n        }\n        out.insert(event, hook_entries);\n    }","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/core/src/import_config.rs#L711-L747","documentation":"In an imported hooks config, the value under a hook event key (e.g. hooks.PreToolUse) is not a JSON array. Each event must map to an array of matcher entries; a non-array (object, string, number) triggers this error. Note the message text with `[].hooks` is slightly misleading — the fault is the event value itself, read from the user-imported config.","triggerScenarios":"Importing a hook group like {\"matcher\": \"Bash\"} without \"hooks\", or {\"matcher\": \"*\", \"hooks\": {\"command\": \"x\"}} where hooks is an object instead of an array.","commonSituations":"Hand-written hook groups omitting the inner \"hooks\" key, flattening the inner array to a single object, or renaming the key (e.g. \"commands\").","solutions":["Add a \"hooks\" array to each hook group containing at least one {\"command\": \"...\"} object","If \"hooks\" is a single object, wrap it in an array","Restore the standard key name \"hooks\" if it was renamed"],"exampleFix":"// before\n{ \"hooks\": { \"PostToolUse\": [ { \"matcher\": \"Bash\", \"command\": \"notify.sh\" } ] } }\n// after\n{ \"hooks\": { \"PostToolUse\": [ { \"matcher\": \"Bash\", \"hooks\": [ { \"command\": \"notify.sh\" } ] } ] } }","handlingStrategy":"validation","validationCode":"for (const [event, groups] of Object.entries(cfg.hooks ?? {})) {\n  for (const g of groups) if (!Array.isArray(g?.hooks)) throw new Error(`hooks.${event} group needs a \"hooks\" array`);\n}","typeGuard":"const hasHooksArray = (g) => typeof g === 'object' && g !== null && Array.isArray(g.hooks);","tryCatchPattern":"try { importConfig(path) } catch (e) { if (/\\[\\]\\.hooks must be an array/.test(String(e))) { console.error('Add \"hooks\": [ { \"command\": \"...\" } ] to each hook group'); } else { throw e; } }","preventionTips":["Every hook group requires the inner \"hooks\" key holding an array","Do not flatten the inner array to a single object when only one hook exists","Copy hook-group examples verbatim rather than abbreviating"],"tags":["json","config-import","validation","hooks"],"backgroundTag":"missing-required-config-field","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}