affaan-m/ECC · error

Invalid : expected hooks. to be an array

Error message

Invalid ${label}: expected hooks.${event} to be an array

What it means

Each value inside settings.hooks must be an array of hook entries. After confirming hooks is an object, the validator iterates its event keys and throws this error if any event's value is not an array. This keeps the hooks structure consistent with Claude's settings schema.

Solutions

  1. Wrap the event's hook entries in an array
  2. Remove the malformed event key if it holds no valid hooks
  3. Re-run validation after fixing to catch any other malformed events
  4. Compare against a known-good settings.json hooks block

Example fix

// before
"Stop": { "type": "command", "command": "x" }
// after
"Stop": [ { "type": "command", "command": "x" } ]
Defensive patterns

Strategy: validation

Validate before calling

const allArrays = (hooks) => Object.values(hooks).every(Array.isArray);

Type guard

const isHookArray = (v) => Array.isArray(v);

Try / catch

try { return parseSettings(s); } catch (e) { const m = /hooks\.(\S+) to be an array/.exec(e.message); if (m) { console.error('Event', m[1], 'must be an array'); } throw e; }

Prevention

When it happens

Trigger: Calling parseSettings/validatedSettings/validateSettings where settings.hooks[event] is a string, object, or number instead of an array — e.g. "hooks": { "Stop": { "command": ... } }.

Common situations: A user flattened a single hook into an object instead of wrapping it in an array, a merge tool replaced an array with a keyed object, or hand-editing removed the [ ] brackets around entries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/842b0fcd0acd099e. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/install/claude-settings.js:229

      }
      seenIds.add(entry.id);
    });
  }
  return cloneValue(managedHooks);
}

function validateSettings(settings, label = 'Claude settings') {
  if (!isJsonObject(settings)) {
    throw new Error(`Invalid ${label}: expected a JSON object`);
  }

  if (Object.prototype.hasOwnProperty.call(settings, 'hooks')) {
    if (!isJsonObject(settings.hooks)) {
      throw new Error(`Invalid ${label}: expected "hooks" to be a JSON object`);
    }
    for (const [event, entries] of Object.entries(settings.hooks)) {
      if (!Array.isArray(entries)) {
        throw new Error(`Invalid ${label}: expected hooks.${event} to be an array`);
      }
    }
  }

  return cloneValue(settings);
}

function replacePluginRootPlaceholders(value, pluginRoot) {
  if (typeof pluginRoot !== 'string') {
    throw new Error('Invalid Claude plugin root: expected a string');
  }
  if (typeof value === 'string') {
    return value.split(PLUGIN_ROOT_PLACEHOLDER).join(pluginRoot);
  }
  if (Array.isArray(value)) {
    return value.map(item => replacePluginRootPlaceholders(item, pluginRoot));
  }
  if (isJsonObject(value)) {

View on GitHub (pinned to 8321021c54)