affaan-m/ECC · error

Invalid managed hook handler at

Error message

Invalid managed hook handler at ${label}: invalid headers

What it means

For http hooks, the optional 'headers' field must be a plain JSON object whose values are all strings. validateHookHandler throws this error when headers is an array, a non-plain object, or contains non-string values (numbers, booleans, nested objects).

Solutions

  1. Make headers a plain object mapping header names to string values: {"Authorization":"Bearer ...","X-Custom":"v1"}.
  2. Stringify any numeric or boolean values: "X-Retries": "3".
  3. Flatten nested header objects into flat string entries.
  4. Ensure the config loader deserializes to plain objects (JSON.parse / safe yaml load with plain schema).

Example fix

// before
{ "type": "http", "url": "https://api.example.com", "headers": { "X-Retries": 3 } }
// after
{ "type": "http", "url": "https://api.example.com", "headers": { "X-Retries": "3" } }
Defensive patterns

Strategy: validation

Validate before calling

const bad = hooks.filter(h=>h.type==='http'&&h.headers!==undefined&&(!isPlainObj(h.headers)||!Object.values(h.headers).every(v=>typeof v==='string'))); if (bad.length) throw new Error('headers must be a string-valued object');

Type guard

const hasValidHeaders = (h) => h.headers === undefined || (typeof h.headers === 'object' && !Array.isArray(h.headers) && Object.values(h.headers).every(v => typeof v === 'string'));

Try / catch

try { installHooks(cfg); } catch (e) { if (e.message.includes('invalid headers')) { console.error('HTTP hook headers must be flat name->string map:', e.message); process.exitCode = 1; } else throw e; }

Prevention

When it happens

Trigger: validateManagedHooks encounters {type:'http', url:'x', headers:[]} or headers:{'X-Retries':3} or headers:{auth:{token:'abc'}} or a class instance from a config loader.

Common situations: Numeric header values (retries, timeouts) that must be strings per HTTP semantics; YAML config where a header table parsed into nested maps; using headers to smuggle config instead of actual header names; passing a Headers instance from fetch instead of a plain object.

Related errors


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

Appendix: source

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

    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`);
    }
    if (
      hook.allowedEnvVars !== undefined
      && (!Array.isArray(hook.allowedEnvVars)
        || !hook.allowedEnvVars.every(isNonEmptyString))
    ) {
      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`);
  }
}

View on GitHub (pinned to 8321021c54)