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

  1. Edit the hooks config so the top-level "hooks" key is a JSON object.
  2. 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)