{"record":{"id":"56ac36a9fcb89951","repo":"affaan-m/ECC","slug":"invalid-label-event-expected-a-non-empty-hook-array","errorCode":null,"errorMessage":"Invalid ${label}.${event}: expected a non-empty hook array","messagePattern":"Invalid (.+?)\\.(.+?): expected a non-empty hook array","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":200,"sourceCode":"          `Invalid managed hook entry at ${label}.${event}[${index}]: expected hooks`\n        );\n      }\n      entry.hooks.forEach((hook, hookIndex) => {\n        validateHookHandler(hook, `${label}.${event}[${index}].hooks[${hookIndex}]`);\n      });\n    });\n  }\n\n  return cloneValue(managedHooks);\n}\n\nfunction validateRecordedManagedHooks(managedHooks, label = 'recorded managed hooks') {\n  if (!isJsonObject(managedHooks) || Object.keys(managedHooks).length === 0) {\n    throw new Error(`Invalid ${label}: expected a non-empty JSON object`);\n  }\n  for (const [event, entries] of Object.entries(managedHooks)) {\n    if (!isNonEmptyString(event) || !Array.isArray(entries) || entries.length === 0) {\n      throw new Error(`Invalid ${label}.${event}: expected a non-empty hook array`);\n    }\n    const seenIds = new Set();\n    entries.forEach((entry, index) => {\n      if (!isJsonObject(entry) || !isNonEmptyString(entry.id) || !Array.isArray(entry.hooks)) {\n        throw new Error(`Invalid hook entry at ${label}.${event}[${index}]`);\n      }\n      if (seenIds.has(entry.id)) {\n        throw new Error(\n          `Invalid ${label}: expected unique id \"${entry.id}\" within event \"${event}\"`\n        );\n      }\n      seenIds.add(entry.id);\n    });\n  }\n  return cloneValue(managedHooks);\n}\n\nfunction validateSettings(settings, label = 'Claude settings') {","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L182-L218","documentation":"This library validates recorded managed hook definitions before writing them into Claude settings. Each hook event key must map to a non-empty array of hook entries. When an event's value is missing, not an array, or an empty array, this error is thrown so malformed hook recordings are rejected early instead of producing broken settings files.","triggerScenarios":"Calling validateRecordedManagedHooks (directly or via validate/findPreviousManagedHooks/previousHooks/recordedHooks) with a managedHooks object where some event key (e.g. \"PreToolUse\") maps to null, a non-array value, or an empty array [].","commonSituations":"A hooks.json was edited by hand and a hook block's matcher entries were deleted, a build script serialized an empty hooks list after filtering, or an older plugin version recorded hooks in a shape the current validator no longer accepts.","solutions":["Inspect the recorded hooks object and ensure every event key maps to a non-empty array of hook entries","If an event legitimately has no hooks, remove the event key entirely instead of leaving an empty array","Regenerate the hooks recording from the source hooks.json rather than hand-editing the recorded settings","If loading from JSON, log/inspect the parsed object and fix the offending event key"],"exampleFix":"// before\n{ \"PreToolUse\": [] }\n// after\n{ \"PreToolUse\": [ { \"id\": \"pre-compact-suggest\", \"hooks\": [ { \"type\": \"command\", \"command\": \"node hook.js\" } ] } ] }","handlingStrategy":"validation","validationCode":"function isValidRecordedHooks(h) { return !!h && typeof h === 'object' && !Array.isArray(h) && Object.entries(h).every(([k, v]) => typeof k === 'string' && Array.isArray(v) && v.length > 0); }","typeGuard":"const isJsonObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":"try { validateRecordedManagedHooks(hooks); } catch (e) { if (String(e.message).includes('non-empty hook array')) { /* drop empty event keys and retry */ } else throw e; }","preventionTips":["Never write empty arrays for hook events; omit the key instead","Validate hooks.json schema in CI before publishing","Regenerate recordings from source rather than hand-editing","Keep hook entries in a typed structure to catch shape drift early"],"tags":["validation","hooks","config"],"backgroundTag":"schema-validation-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}