{"record":{"id":"054a932b80f122b7","repo":"affaan-m/ECC","slug":"invalid-managed-hook-entry-at-label-event-index-expected-a","errorCode":null,"errorMessage":"Invalid managed hook entry at ${label}.${event}[${index}]: expected a JSON object","messagePattern":"Invalid managed hook entry at (.+?)\\.(.+?)\\[(.+?)\\]: expected a JSON object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":149,"sourceCode":"  if (Object.keys(managedHooks).length === 0) {\n    throw new Error(`Invalid ${label}: expected at least one hook event`);\n  }\n\n  const seenIds = new Set();\n  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(","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L131-L167","documentation":"Each hook entry inside an event array must be a plain JSON object. validateManagedHooks throws this when an entry is a string, number, array, null, or a non-plain object (e.g. a class instance whose prototype is not Object.prototype). Entries must carry the structured fields (id, matcher, hooks) this library manages.","triggerScenarios":"Passing an event array whose elements are raw command strings like { PreToolUse: ['fmt.sh'] }, a null element, or an object created with a custom class/prototype.","commonSituations":"Condensing entries to shorthand strings when hand-editing; spreading an array into the wrong position; using JSON parsed with a reviver that returns class instances; passing a Map instead of a plain object.","solutions":["Replace each non-object entry with a full hook-entry object: { id, matcher, hooks: [...] }.","Check the index in the message (label.event[index]) to locate the offending element.","If passing programmatically-created objects, ensure they are plain object literals (Object.getPrototypeOf(v) === Object.prototype || null).","Regenerate the entries via the ECC install tooling."],"exampleFix":"// before\nconst hooks = { PreToolUse: ['npx fmt'] };\n// after\nconst hooks = { PreToolUse: [{ id: 'fmt', matcher: 'Bash', hooks: [{ type: 'command', command: 'npx fmt' }] }] };","handlingStrategy":"type-guard","validationCode":"managedHooks && Object.values(managedHooks).flat().forEach((e, i) => {\n  if (e === null || typeof e !== 'object' || Array.isArray(e)) {\n    throw new Error(`Entry ${i} is not a plain object`);\n  }\n});","typeGuard":"function isPlainObject(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v)\n    && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);\n}","tryCatchPattern":"try {\n  managedHooks(hooks);\n} catch (err) {\n  if (/expected a JSON object/.test(err.message)) {\n    console.error('A hook entry is not a plain object — use { id, matcher, hooks: [...] }');\n  }\n  throw err;\n}","preventionTips":["Never use shorthand strings for hook entries","Use JSON.parse/JSON.stringify round-trip to normalize to plain objects","Avoid class instances and Maps in config values","Validate every element with isPlainObject before calling the API"],"tags":["validation","type-mismatch","hooks"],"backgroundTag":"type-mismatch","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"}