affaan-m/ECC · error

Invalid . : expected a non-empty hook array

Error message

Invalid ${label}.${event}: expected a non-empty hook array

What it means

This library validates recorded managed hook definitions before writing them into Claude settings. Each hook event key must map to a non-empty array of hook entries. When an event's value is missing, not an array, or an empty array, this error is thrown so malformed hook recordings are rejected early instead of producing broken settings files.

Solutions

  1. Inspect the recorded hooks object and ensure every event key maps to a non-empty array of hook entries
  2. If an event legitimately has no hooks, remove the event key entirely instead of leaving an empty array
  3. Regenerate the hooks recording from the source hooks.json rather than hand-editing the recorded settings
  4. If loading from JSON, log/inspect the parsed object and fix the offending event key

Example fix

// before
{ "PreToolUse": [] }
// after
{ "PreToolUse": [ { "id": "pre-compact-suggest", "hooks": [ { "type": "command", "command": "node hook.js" } ] } ] }
Defensive patterns

Strategy: validation

Validate before calling

function isValidRecordedHooks(h) { return !!h && typeof h === 'object' && !Array.isArray(h) && Object.entries(h).every(([k, v]) => typeof k === 'string' && Array.isArray(v) && v.length > 0); }

Type guard

const isJsonObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);

Try / catch

try { validateRecordedManagedHooks(hooks); } catch (e) { if (String(e.message).includes('non-empty hook array')) { /* drop empty event keys and retry */ } else throw e; }

Prevention

When it happens

Trigger: Calling validateRecordedManagedHooks (directly or via validate/findPreviousManagedHooks/previousHooks/recordedHooks) with a managedHooks object where some event key (e.g. "PreToolUse") maps to null, a non-array value, or an empty array [].

Common situations: A hooks.json was edited by hand and a hook block's matcher entries were deleted, a build script serialized an empty hooks list after filtering, or an older plugin version recorded hooks in a shape the current validator no longer accepts.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

          `Invalid managed hook entry at ${label}.${event}[${index}]: expected hooks`
        );
      }
      entry.hooks.forEach((hook, hookIndex) => {
        validateHookHandler(hook, `${label}.${event}[${index}].hooks[${hookIndex}]`);
      });
    });
  }

  return cloneValue(managedHooks);
}

function validateRecordedManagedHooks(managedHooks, label = 'recorded managed hooks') {
  if (!isJsonObject(managedHooks) || Object.keys(managedHooks).length === 0) {
    throw new Error(`Invalid ${label}: expected a non-empty JSON object`);
  }
  for (const [event, entries] of Object.entries(managedHooks)) {
    if (!isNonEmptyString(event) || !Array.isArray(entries) || entries.length === 0) {
      throw new Error(`Invalid ${label}.${event}: expected a non-empty hook array`);
    }
    const seenIds = new Set();
    entries.forEach((entry, index) => {
      if (!isJsonObject(entry) || !isNonEmptyString(entry.id) || !Array.isArray(entry.hooks)) {
        throw new Error(`Invalid hook entry at ${label}.${event}[${index}]`);
      }
      if (seenIds.has(entry.id)) {
        throw new Error(
          `Invalid ${label}: expected unique id "${entry.id}" within event "${event}"`
        );
      }
      seenIds.add(entry.id);
    });
  }
  return cloneValue(managedHooks);
}

function validateSettings(settings, label = 'Claude settings') {

View on GitHub (pinned to 8321021c54)