affaan-m/ECC · error

Invalid managed hook handler at

Error message

Invalid managed hook handler at ${label}: unsupported type

What it means

validateHookHandler requires each handler's 'type' field to be one of the VALID_HOOK_TYPES supported by ECC's managed hooks. An object handler with a missing or unrecognized type (e.g. 'script', 'shell', or undefined) is rejected so the installer never writes hook entries Claude cannot interpret.

Solutions

  1. Set the handler's 'type' to a supported value (e.g. "command") at the labeled location.
  2. Check VALID_HOOK_TYPES in scripts/lib/install/claude-settings.js for the exact accepted set.
  3. Update ECC if your hooks schema is newer than the installed version's validator.

Example fix

// before
{ "type": "shell", "command": "node my-hook.js" }
// after
{ "type": "command", "command": "node my-hook.js" }
Defensive patterns

Strategy: validation

Validate before calling

import { VALID_HOOK_TYPES } from './scripts/lib/install/claude-settings.js';
for (const handlers of Object.values(settings.hooks ?? {})) {
  for (const h of handlers) {
    if (!VALID_HOOK_TYPES.has(h.type)) throw new Error(`unsupported hook type: ${h.type}`);
  }
}

Type guard

const hasValidHookType = (h, valid) => typeof h?.type === 'string' && valid.has(h.type);

Try / catch

try {
  validateManagedHooks(hooks);
} catch (e) {
  if (e.message.includes('unsupported type')) {
    console.error('Use one of:', [...VALID_HOOK_TYPES].join(', '));
  } else throw e;
}

Prevention

When it happens

Trigger: validateManagedHooks processes a handler object whose hook.type is not in VALID_HOOK_TYPES — missing type field, typo'd type, or a type from a newer/older Claude hooks schema.

Common situations: Copying hook examples from docs for other tools that use different type names; editing type by hand; schema drift after a Claude Code hooks format change.

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/20ecfcfe0b4e7677. Report an issue: GitHub.

Appendix: source

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

function assertClaudeSettingsPath(destinationPath, trustedRoot) {
  const resolvedDestination = path.resolve(destinationPath);
  const resolvedExpected = path.resolve(getClaudeSettingsPath(trustedRoot));
  const pathsMatch = process.platform === 'win32'
    ? resolvedDestination.toLowerCase() === resolvedExpected.toLowerCase()
    : resolvedDestination === resolvedExpected;
  if (!pathsMatch) {
    throw new Error(
      `Refusing to manage Claude hooks outside the canonical settings file: ${destinationPath}`
    );
  }
}

function validateHookHandler(hook, label) {
  if (!isJsonObject(hook)) {
    throw new Error(`Invalid managed hook handler at ${label}: expected a JSON object`);
  }
  if (!VALID_HOOK_TYPES.has(hook.type)) {
    throw new Error(`Invalid managed hook handler at ${label}: unsupported type`);
  }
  if (hook.timeout !== undefined && (typeof hook.timeout !== 'number' || hook.timeout < 0)) {
    throw new Error(`Invalid managed hook handler at ${label}: invalid timeout`);
  }

  if (hook.type === 'command') {
    const validCommand = isNonEmptyString(hook.command)
      || (Array.isArray(hook.command)
        && hook.command.length > 0
        && hook.command.every(isNonEmptyString));
    if (!validCommand) {
      throw new Error(`Invalid managed hook handler at ${label}: invalid command`);
    }
    if (hook.async !== undefined && typeof hook.async !== 'boolean') {
      throw new Error(`Invalid managed hook handler at ${label}: invalid async flag`);
    }
    return;
  }

View on GitHub (pinned to 8321021c54)