{"record":{"id":"4d798eb9776dc487","repo":"affaan-m/ECC","slug":"invalid-managed-hook-handler-at-label-expected-a-json-object","errorCode":null,"errorMessage":"Invalid managed hook handler at ${label}: expected a JSON object","messagePattern":"Invalid managed hook handler at (.+?): expected a JSON object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":73,"sourceCode":"  return path.join(targetRoot, CLAUDE_SETTINGS_FILENAME);\n}\n\nfunction assertClaudeSettingsPath(destinationPath, trustedRoot) {\n  const resolvedDestination = path.resolve(destinationPath);\n  const resolvedExpected = path.resolve(getClaudeSettingsPath(trustedRoot));\n  const pathsMatch = process.platform === 'win32'\n    ? resolvedDestination.toLowerCase() === resolvedExpected.toLowerCase()\n    : resolvedDestination === resolvedExpected;\n  if (!pathsMatch) {\n    throw new Error(\n      `Refusing to manage Claude hooks outside the canonical settings file: ${destinationPath}`\n    );\n  }\n}\n\nfunction validateHookHandler(hook, label) {\n  if (!isJsonObject(hook)) {\n    throw new Error(`Invalid managed hook handler at ${label}: expected a JSON object`);\n  }\n  if (!VALID_HOOK_TYPES.has(hook.type)) {\n    throw new Error(`Invalid managed hook handler at ${label}: unsupported type`);\n  }\n  if (hook.timeout !== undefined && (typeof hook.timeout !== 'number' || hook.timeout < 0)) {\n    throw new Error(`Invalid managed hook handler at ${label}: invalid timeout`);\n  }\n\n  if (hook.type === 'command') {\n    const validCommand = isNonEmptyString(hook.command)\n      || (Array.isArray(hook.command)\n        && hook.command.length > 0\n        && hook.command.every(isNonEmptyString));\n    if (!validCommand) {\n      throw new Error(`Invalid managed hook handler at ${label}: invalid command`);\n    }\n    if (hook.async !== undefined && typeof hook.async !== 'boolean') {\n      throw new Error(`Invalid managed hook handler at ${label}: invalid async flag`);","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L55-L91","documentation":"validateHookHandler checks each managed hook handler against the expected Claude hook-handler schema. A handler that is not a JSON object (string, array, number, null) fails the very first check. The label in the message identifies where in the hooks structure the bad value sits.","triggerScenarios":"validateManagedHooks encounters a hook handler value that is not a plain object — e.g. a hook entry defined as a string command instead of an object with type/command fields.","commonSituations":"Hand-written settings.json where a hook was written as \"node hook.js\" instead of {\"type\":\"command\",\"command\":\"node hook.js\"}; JSON produced by another tool using a different hook schema.","solutions":["Rewrite the handler at the labeled location as a JSON object with a valid 'type' field.","Validate the settings JSON structure (jq or a schema check) before running the install.","Let the ECC installer generate hook entries instead of hand-authoring them."],"exampleFix":"// before\n\"hooks\": { \"PreToolUse\": [\"node my-hook.js\"] }\n// after\n\"hooks\": { \"PreToolUse\": [{ \"type\": \"command\", \"command\": \"node my-hook.js\" }] }","handlingStrategy":"validation","validationCode":"const isHookHandlerObject = (h) => typeof h === 'object' && h !== null && !Array.isArray(h);\nfor (const handlers of Object.values(settings.hooks ?? {})) {\n  for (const h of handlers) if (!isHookHandlerObject(h)) throw new Error('hook handler must be an object');\n}","typeGuard":"const isJsonObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":"try {\n  validateManagedHooks(hooks);\n} catch (e) {\n  if (e.message.includes('expected a JSON object')) {\n    console.error('Fix the hook handler shape at:', e.message.match(/at (.+?):/)?.[1]);\n  } else throw e;\n}","preventionTips":["Author hooks as objects: { type, command, timeout? } — never bare strings.","Run jq or a JSON schema check on settings.json before installing.","Let ECC scripts generate hook entries rather than hand-writing them."],"tags":["hooks","validation","schema","json"],"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"}