affaan-m/ECC · error

Invalid managed hook handler at

Error message

Invalid managed hook handler at ${label}: invalid async flag

What it means

For command-type hooks, the optional 'async' flag must be a boolean. validateHookHandler throws this error when hook.async is present on a {type:'command'} handler but is not true or false (e.g. a string "true", a number, or null).

Solutions

  1. Change the async value to a real JSON boolean: true or false.
  2. Remove the async field entirely if you do not need it (undefined is accepted).
  3. Fix your config generator to emit booleans, not strings (JSON.stringify the value, don't String() it).
  4. Re-validate the settings object before writing it.

Example fix

// before
{ "type": "command", "command": "npm test", "async": "true" }
// after
{ "type": "command", "command": "npm test", "async": true }
Defensive patterns

Strategy: validation

Validate before calling

const bad = hooks.filter(h=>h.type==='command'&&h.async!==undefined&&typeof h.async!=='boolean'); if (bad.length) throw new Error('async must be boolean on: '+bad.map(JSON.stringify));

Type guard

const hasValidAsync = (h) => h.async === undefined || typeof h.async === 'boolean';

Try / catch

try { installHooks(cfg); } catch (e) { if (e.message.includes('invalid async flag')) { e.message += ' — check that async is a literal true/false, not a quoted string'; throw e; } throw e; }

Prevention

When it happens

Trigger: validateManagedHooks encounters a command handler like {type:'command', command:'x', async:'true'} or async:1 or async:null.

Common situations: Hand-editing settings.json and quoting the boolean ('async': "true"); generating hooks from a template engine that stringifies booleans; copying config from docs where async appeared as a YAML unquoted true that got serialized as a string.

Related errors


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

Appendix: source

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

    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;
  }

  if (hook.async !== undefined) {
    throw new Error(`Invalid managed hook handler at ${label}: async requires command type`);
  }
  if (hook.type === 'http') {
    if (!isNonEmptyString(hook.url)) {
      throw new Error(`Invalid managed hook handler at ${label}: invalid url`);
    }
    if (
      hook.headers !== undefined
      && (!isJsonObject(hook.headers)
        || !Object.values(hook.headers).every(value => typeof value === 'string'))
    ) {
      throw new Error(`Invalid managed hook handler at ${label}: invalid headers`);
    }

View on GitHub (pinned to 8321021c54)