{"record":{"id":"8696ce5656b1feed","repo":"affaan-m/ECC","slug":"invalid-managed-hook-entry-at-label-event-index-expected-a-8696ce","errorCode":null,"errorMessage":"Invalid managed hook entry at ${label}.${event}[${index}]: expected a non-empty unique id","messagePattern":"Invalid managed hook entry at (.+?)\\.(.+?)\\[(.+?)\\]: expected a non-empty unique id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":154,"sourceCode":"  for (const [event, entries] of Object.entries(managedHooks)) {\n    if (!VALID_EVENTS.has(event)) {\n      throw new Error(`Invalid ${label}: unsupported hook event \"${event}\"`);\n    }\n    if (!Array.isArray(entries)) {\n      throw new Error(`Invalid ${label}.${event}: expected an array`);\n    }\n    if (entries.length === 0) {\n      throw new Error(`Invalid ${label}.${event}: expected at least one hook entry`);\n    }\n\n    entries.forEach((entry, index) => {\n      if (!isJsonObject(entry)) {\n        throw new Error(\n          `Invalid managed hook entry at ${label}.${event}[${index}]: expected a JSON object`\n        );\n      }\n      if (typeof entry.id !== 'string' || entry.id.trim() === '') {\n        throw new Error(\n          `Invalid managed hook entry at ${label}.${event}[${index}]: `\n          + 'expected a non-empty unique id'\n        );\n      }\n      if (seenIds.has(entry.id)) {\n        throw new Error(`Invalid ${label}: expected globally unique id \"${entry.id}\"`);\n      }\n      seenIds.add(entry.id);\n      if (\n        !Object.prototype.hasOwnProperty.call(entry, 'matcher')\n        && !EVENTS_WITHOUT_MATCHER.has(event)\n      ) {\n        throw new Error(\n          `Invalid managed hook entry at ${label}.${event}[${index}]: missing matcher`\n        );\n      }\n      if (\n        Object.prototype.hasOwnProperty.call(entry, 'matcher')","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L136-L172","documentation":"Every hook entry must have an id that is a non-empty (non-whitespace) string. validateManagedHooks throws this when id is missing, not a string, an empty string, or whitespace-only. The id is the stable identifier ECC uses to match, diff, and remove managed hooks across installs, so it cannot be absent or blank.","triggerScenarios":"Passing an entry like { matcher: 'Bash', hooks: [...] } without an id field; id: '' or id: '   '; id set to a number or null.","commonSituations":"Hand-writing a new hook entry and forgetting the id; copying a Claude-Code-native hooks block (which has no id field) into the managed hooks map; trimming/normalization accidentally emptying the id.","solutions":["Add a unique non-empty string id to the entry, e.g. id: 'my-hook-lint'.","Use the index from the message (label.event[index]) to find the entry missing the id.","When importing existing Claude hooks, assign your own ids to each entry.","Adopt a naming convention (e.g. 'ecc-<purpose>') so ids stay unique and recognizable."],"exampleFix":"// before\n{ matcher: 'Bash', hooks: [{ type: 'command', command: 'lint' }] }\n// after\n{ id: 'ecc-lint-bash', matcher: 'Bash', hooks: [{ type: 'command', command: 'lint' }] }","handlingStrategy":"validation","validationCode":"for (const entries of Object.values(managedHooks)) {\n  entries.forEach((e, i) => {\n    if (typeof e.id !== 'string' || e.id.trim() === '') {\n      throw new Error(`Entry ${i} needs a non-empty string id`);\n    }\n  });\n}","typeGuard":"function allEntriesHaveIds(hooks) {\n  return Object.values(hooks).flat().every(e =>\n    e !== null && typeof e === 'object' && typeof e.id === 'string' && e.id.trim() !== '');\n}","tryCatchPattern":"try {\n  materializeManagedHooks(hooks);\n} catch (err) {\n  if (/non-empty unique id/.test(err.message)) {\n    const m = err.message.match(/\\.(\\w+)\\[(\\d+)\\]/);\n    console.error(`Entry ${m?.[2]} in ${m?.[1]} is missing id — add e.g. id: 'ecc-my-hook'`);\n  }\n  throw err;\n}","preventionTips":["Adopt an id naming convention (ecc-<purpose>)","Assign ids when importing external/Claude-native hooks — they lack id fields","Never set id to empty string or whitespace","Validate ids in CI alongside the hook config"],"tags":["validation","identifier","hooks"],"backgroundTag":"empty-required-field","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"}