affaan-m/ECC · error

Invalid : expected at least one hook event

Error message

Invalid ${label}: expected at least one hook event

What it means

A managed hooks config must contain at least one hook event key. validateManagedHooks throws this error when the config is a plain object but has zero keys ({}), because installing an empty hook set via this API is treated as a mistake rather than a no-op.

Solutions

  1. Provide at least one hook event in the object, e.g. {"SessionStart":[{type:'command', command:'...'}]}.
  2. If you intend to remove all hooks, use the API's removal/uninstall path rather than validating an empty desired set.
  3. Fix upstream filters so they do not silently drop every hook; log when the filtered set is empty.
  4. Generate hooks before validation, or short-circuit the install call when the hook list is empty.

Example fix

// before
validateManagedHooks({});
// after
const hooks = { "SessionStart": [{ "type": "command", "command": "node hooks/session-start.js" }] };
validateManagedHooks(hooks);
Defensive patterns

Strategy: validation

Validate before calling

if (Object.keys(desiredHooks).length === 0) { throw new Error('Refusing to install an empty managed hooks set — use the uninstall API or fix the filter'); }

Type guard

const nonEmptyHooks = (v) => v !== null && typeof v === 'object' && Object.keys(v).length > 0;

Try / catch

try { installHooks(desired); } catch (e) { if (e.message.includes('expected at least one hook event')) { console.error('Desired hooks resolved to empty — check filters/generators:', e.message); process.exitCode = 1; } else throw e; }

Prevention

When it happens

Trigger: Calling materializeManagedHooks/validateManagedHooks with {} or an object whose keys were all filtered out; computing a diff where all hooks were removed and passing the resulting empty object as the desired set.

Common situations: A filter step (by event, by agent) removed every hook before install; a fresh project generating hooks from an empty list; migrating configs where the source hooks block was empty; conditional code that builds hooks but skips adding any.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

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

View on GitHub (pinned to 8321021c54)