affaan-m/ECC · error

Invalid . : expected an array

Error message

Invalid ${label}.${event}: expected an array

What it means

After confirming the event name is valid, validateManagedHooks requires the value under each event key to be an array of hook entries. This error fires when the value is any other type (object, string, number, null, undefined). It protects the later per-entry validation from operating on a non-iterable/mis-shaped value.

Solutions

  1. Wrap the hook entries in an array under the offending event key.
  2. Re-read the error text: label.event identifies exactly which key has the wrong shape.
  3. Re-run the ECC installer to regenerate the managed-hooks block rather than hand-merging old settings.json content.
  4. Validate the shape with Array.isArray before calling the API.

Example fix

// before
const hooks = { PreToolUse: { id: 'fmt', matcher: 'Bash', hooks: [{ type: 'command', command: 'fmt' }] } };
// after
const hooks = { PreToolUse: [{ id: 'fmt', matcher: 'Bash', hooks: [{ type: 'command', command: 'fmt' }] }] };
Defensive patterns

Strategy: type-guard

Validate before calling

for (const [event, entries] of Object.entries(managedHooks)) {
  if (!Array.isArray(entries)) throw new Error(`hooks.${event} must be an array`);
}

Type guard

function isHooksMap(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v)
    && Object.values(v).every(Array.isArray);
}

Try / catch

try {
  validateManagedHooks(hooks);
} catch (err) {
  if (/expected an array/.test(err.message)) {
    const event = err.message.match(/\.(\w+):/)?.[1];
    if (event && !Array.isArray(hooks[event])) hooks[event] = [hooks[event]];
  }
  throw err;
}

Prevention

When it happens

Trigger: Passing a hooks map where an event maps to something other than an array, e.g. { PreToolUse: { id: 'x', ... } } (single entry not wrapped in an array) or { Stop: 'lint.sh' }.

Common situations: Hand-merging two hook configs and dropping the array wrapper; JSON with a single object instead of a list under an event; a schema change in an older ECC version whose persisted format used objects per event.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

    throw new Error(`Invalid managed hook handler at ${label}: invalid model`);
  }
}

function validateManagedHooks(managedHooks, label = 'managed hooks') {
  if (!isJsonObject(managedHooks)) {
    throw new Error(`Invalid ${label}: expected a JSON object`);
  }
  if (Object.keys(managedHooks).length === 0) {
    throw new Error(`Invalid ${label}: expected at least one hook event`);
  }

  const seenIds = new Set();
  for (const [event, entries] of Object.entries(managedHooks)) {
    if (!VALID_EVENTS.has(event)) {
      throw new Error(`Invalid ${label}: unsupported hook event "${event}"`);
    }
    if (!Array.isArray(entries)) {
      throw new Error(`Invalid ${label}.${event}: expected an array`);
    }
    if (entries.length === 0) {
      throw new Error(`Invalid ${label}.${event}: expected at least one hook entry`);
    }

    entries.forEach((entry, index) => {
      if (!isJsonObject(entry)) {
        throw new Error(
          `Invalid managed hook entry at ${label}.${event}[${index}]: expected a JSON object`
        );
      }
      if (typeof entry.id !== 'string' || entry.id.trim() === '') {
        throw new Error(
          `Invalid managed hook entry at ${label}.${event}[${index}]: `
          + 'expected a non-empty unique id'
        );
      }
      if (seenIds.has(entry.id)) {

View on GitHub (pinned to 8321021c54)