{"record":{"id":"a6612fbdd8a9c415","repo":"affaan-m/ECC","slug":"invalid-hooks-config-expected-a-json-object-with-a-hooks","errorCode":null,"errorMessage":"Invalid hooks config: expected a JSON object with a hooks object","messagePattern":"Invalid hooks config: expected a JSON object with a hooks object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":299,"sourceCode":"        ...entry,\n        hooks: entry.hooks.map(hook => ({\n          ...hook,\n          ...(typeof hook.command === 'string' || Array.isArray(hook.command)\n            ? {\n              command: Array.isArray(hook.command)\n                ? hook.command.map(resolveCommand)\n                : resolveCommand(hook.command),\n            }\n            : {}),\n        })),\n      })),\n    ])\n  );\n}\n\nfunction materializeManagedHooks(hooksConfig, targetRoot) {\n  if (!isJsonObject(hooksConfig) || !isJsonObject(hooksConfig.hooks)) {\n    throw new Error('Invalid hooks config: expected a JSON object with a hooks object');\n  }\n  if (!isNonEmptyString(targetRoot)) {\n    throw new Error('Invalid Claude target root: expected a non-empty string');\n  }\n  return validateManagedHooks(resolveManagedHookCommands(\n    replacePluginRootPlaceholders(hooksConfig.hooks, targetRoot),\n    targetRoot\n  ));\n}\n\nfunction parseSettings(rawSettings, label = 'Claude settings') {\n  let settings;\n  try {\n    settings = JSON.parse(rawSettings);\n  } catch (error) {\n    throw new Error(`Failed to parse ${label}: ${error.message}`, { cause: error });\n  }\n  return validateSettings(settings, label);","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L281-L317","documentation":"materializeManagedHooks turns a hooks config (as found in hooks.json) into concrete managed hooks for a target root. It requires the config to be a JSON object containing a hooks object; anything else cannot be materialized, so this error is thrown up front. This catches wrong files or wrong config shapes before path substitution and validation run.","triggerScenarios":"Calling materializeManagedHooks with hooksConfig that is not an object, or whose .hooks property is missing or not an object — e.g. passing the parsed settings.json instead of hooks.json, or a hooks.json without a top-level \"hooks\" key.","commonSituations":"Pointing the installer at the wrong JSON file, a plugin's hooks.json missing the hooks wrapper after a bad edit, or passing a string of raw JSON instead of a parsed object.","solutions":["Pass the parsed hooks.json object which has a top-level hooks object","If you have raw text, JSON.parse it first and confirm a hooks key exists","Verify the file path points to hooks.json, not settings.json","Add a pre-check: config && typeof config.hooks === 'object'"],"exampleFix":"// before\nmaterializeManagedHooks(JSON.parse(await fs.readFile('hooks.json', 'utf8')).hooks, root)\n// after\nconst cfg = JSON.parse(await fs.readFile('hooks.json', 'utf8'));\nmaterializeManagedHooks(cfg, root) // pass the wrapper, not cfg.hooks","handlingStrategy":"validation","validationCode":"const isHooksConfig = (c) => c && typeof c === 'object' && !Array.isArray(c) && c.hooks && typeof c.hooks === 'object' && !Array.isArray(c.hooks);","typeGuard":"const hasHooksObject = (v) => typeof v === 'object' && v !== null && isJsonObject(v.hooks);","tryCatchPattern":"try { return materializeManagedHooks(cfg, root); } catch (e) { if (e.message.includes('expected a JSON object with a hooks object')) { console.error('Passed value is not a hooks.json wrapper; check the file path'); } throw e; }","preventionTips":["Pass the whole parsed hooks.json object, not its .hooks field","Confirm the loaded file is hooks.json (top-level hooks key)","JSON.parse raw text before calling the library","Schema-check hooks.json in CI"],"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"}