{"record":{"id":"cd7230a19fed68c2","repo":"affaan-m/ECC","slug":"invalid-label-expected-unique-id-entry-id-within-event-event","errorCode":null,"errorMessage":"Invalid ${label}: expected unique id \"${entry.id}\" within event \"${event}\"","messagePattern":"Invalid (.+?): expected unique id \"(.+?)\" within event \"(.+?)\"","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":208,"sourceCode":"\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') {\n  if (!isJsonObject(settings)) {\n    throw new Error(`Invalid ${label}: expected a JSON object`);\n  }\n\n  if (Object.prototype.hasOwnProperty.call(settings, 'hooks')) {\n    if (!isJsonObject(settings.hooks)) {\n      throw new Error(`Invalid ${label}: expected \"hooks\" to be a JSON object`);\n    }","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L190-L226","documentation":"Within a single hook event, each recorded entry must have a unique id. The validator tracks seen ids per event and throws when a duplicate appears, preventing ambiguous hook identity that would break deduplication and idempotent installs of managed hooks into Claude settings.","triggerScenarios":"Calling validateRecordedManagedHooks (or its callers validate/previousHooks/recordedHooks) where two entries under the same event key share the same id string.","commonSituations":"Merging two hooks.json sources that both use the default id, copy-pasting an entry block without changing its id, or a plugin upgrade appending its hooks again onto existing settings.","solutions":["Rename one of the duplicate ids so each entry within the event is unique","Deduplicate entries before validation (keep the newest/first by id)","Regenerate the recording after fixing the source hooks.json","If merging recordings programmatically, key entries by id and overwrite instead of concatenating"],"exampleFix":"// before\n[ { \"id\": \"fmt\", ... }, { \"id\": \"fmt\", ... } ]\n// after\n[ { \"id\": \"fmt\", ... }, { \"id\": \"fmt-v2\", ... } ]","handlingStrategy":"validation","validationCode":"function hasUniqueIds(entries) { const s = new Set(); return entries.every(e => !s.has(e.id) && s.add(e.id)); }","typeGuard":"null","tryCatchPattern":"try { validateRecordedManagedHooks(hooks); } catch (e) { if (e.message.includes('expected unique id')) { hooks = dedupeById(hooks); validateRecordedManagedHooks(hooks); } else throw e; }","preventionTips":["Derive ids from a naming convention to avoid collisions","Deduplicate by id when merging hook sources","Never copy-paste hook entries without renaming the id","Check for duplicate ids in CI on hooks.json"],"tags":["validation","hooks","duplicate-ids"],"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"}