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
- Make headers a plain object mapping header names to string values: {"Authorization":"Bearer ...","X-Custom":"v1"}.
- Stringify any numeric or boolean values: "X-Retries": "3".
- Flatten nested header objects into flat string entries.
- 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
- Stringify all header values, including numbers.
- Do not pass fetch Headers instances; use plain objects.
- Flatten nested header groups before install.
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
- Invalid : expected a JSON object
- Invalid managed hook handler at
- Invalid managed hook handler at
- application config exceeds local size limit
- Asset request_id/modality does not match the bundle
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)