affaan-m/ECC · error · Error
Invalid hooks config at
Error message
Invalid hooks config at ${hooksSourcePath}: expected "hooks" to be a JSON object What it means
The hooks.json staged for a Claude/Claude-project install parsed to a JSON object, but its 'hooks' member is not itself a JSON object. The hooks rewriter needs the hooks key to be a mapping keyed by event name to substitute plugin-root placeholders.
Solutions
- Edit the hooks config so the top-level "hooks" key is a JSON object.
- Restore hooks.json from the repository if it was hand-edited incorrectly.
Example fix
node -e "const c=require('<hooksSourcePath>'); if(typeof c.hooks!=='object') throw new Error('fix hooks')" Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at scripts/lib/install/apply.js:309 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/fb375e6905ba4687.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/apply.js:309
if (!plan.adapter || (plan.adapter.target !== 'claude' && plan.adapter.target !== 'claude-project')) {
return null;
}
const pluginRoot = plan.targetRoot;
const hooksDestinationPath = path.join(plan.targetRoot, 'hooks', 'hooks.json');
const hooksOperation = findHooksOperation(plan, hooksDestinationPath);
if (!hooksOperation) {
return null;
}
const hooksSourcePath = hooksOperation.sourcePath;
if (!fs.existsSync(hooksSourcePath)) {
return null;
}
const hooksConfig = readJsonObject(hooksSourcePath, 'hooks config');
const resolvedHooks = replacePluginRootPlaceholders(hooksConfig.hooks, pluginRoot);
if (!resolvedHooks || typeof resolvedHooks !== 'object' || Array.isArray(resolvedHooks)) {
throw new Error(`Invalid hooks config at ${hooksSourcePath}: expected "hooks" to be a JSON object`);
}
return {
hooksOperation,
hooksDestinationPath,
resolvedHooksConfig: {
...hooksConfig,
hooks: resolvedHooks,
},
};
}
function previewInstallPlan(plan) {
const migration = prepareClaudeSkillMigration(plan);
return {
...plan,
statePreview: migration.finalState,
plannedOperations: [...plan.operations],View on GitHub (pinned to 06c5e118c4)