affaan-m/ECC · error

Invalid : unsupported hook event

Error message

Invalid ${label}: unsupported hook event "${event}"

What it means

validateManagedHooks rejects any top-level key of the managed hooks map that is not one of the known Claude hook event names (VALID_EVENTS, e.g. PreToolUse, PostToolUse, SessionStart). The thrown message embeds the offending key so the developer can see which event name was rejected. This guard ensures ECC only materializes hooks Claude Code actually recognizes, preventing silently-ignored or corrupt settings.json content.

Solutions

  1. Fix the event key to an exact PascalCase name from VALID_EVENTS (SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, Notification, SubagentStart, Stop, SubagentStop, PreCompact, InstructionsLoaded, TeammateIdle, TaskCompleted, ConfigChange, WorktreeCreate, WorktreeRemove, SessionEnd).
  2. Check the message's quoted "${event}" value and compare character-by-character against the valid list (watch casing and spacing).
  3. Regenerate the hooks map via the ECC install tooling instead of hand-editing, then re-run the operation.
  4. If a genuinely new event is needed, add it to VALID_EVENTS in scripts/lib/install/claude-settings.js.

Example fix

// before
const managedHooks = { PostToolUseFailure2: [{ id: 'lint', matcher: '*', hooks: [...] }] };
// after
const managedHooks = { PostToolUseFailure: [{ id: 'lint', matcher: '*', hooks: [...] }] };
Defensive patterns

Strategy: validation

Validate before calling

const VALID = new Set(['SessionStart','UserPromptSubmit','PreToolUse','PermissionRequest','PostToolUse','PostToolUseFailure','Notification','SubagentStart','Stop','SubagentStop','PreCompact','InstructionsLoaded','TeammateIdle','TaskCompleted','ConfigChange','WorktreeCreate','WorktreeRemove','SessionEnd']);
const bad = Object.keys(managedHooks).filter(e => !VALID.has(e));
if (bad.length) throw new Error(`Unknown hook events: ${bad.join(', ')}`);

Type guard

function hasOnlyValidEvents(hooks) {
  return hooks !== null && typeof hooks === 'object' && !Array.isArray(hooks)
    && Object.keys(hooks).every(e => VALID_EVENT_SET.has(e));
}

Try / catch

try {
  materializeManagedHooks(hooks);
} catch (err) {
  if (/unsupported hook event/.test(err.message)) {
    const bad = err.message.match(/"([^"]+)"/)?.[1];
    console.error(`Fix event name "${bad}" — must be PascalCase Claude event (e.g. PreToolUse)`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling any of managedHooks, previousManagedHooks, inspectManagedOperation, materializeManagedHooks, desiredHooks, or expectedHooks with a hooks map containing a misspelled or invented event key, e.g. { PostTool: [...] } or { preToolUse: [...] } (wrong casing).

Common situations: Typo in a hand-edited hooks definition; snake_case/camelCase event names instead of PascalCase; event names copied from another tool (e.g. VS Code or GitHub Actions event names); a Claude Code hook event renamed across versions while an old config lingers.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

    throw new Error(`Invalid managed hook handler at ${label}: invalid prompt`);
  }
  if (hook.model !== undefined && !isNonEmptyString(hook.model)) {
    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'

View on GitHub (pinned to 8321021c54)