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
- 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).
- Check the message's quoted "${event}" value and compare character-by-character against the valid list (watch casing and spacing).
- Regenerate the hooks map via the ECC install tooling instead of hand-editing, then re-run the operation.
- 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
- Keep a shared constant of valid event names and build configs from it
- Never hand-type event names — copy from VALID_EVENTS
- Watch casing: events are PascalCase (PreToolUse, not preToolUse)
- Lint hook configs against VALID_EVENTS in CI before install
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
- Choose at least one guided harness: Claude, Codex, or Kimi.
- failure.reason
- genre has no references
- Invalid Claude hooks preference
- Invalid hook entry at
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)