affaan-m/ECC · error
Invalid managed hook handler at
Error message
Invalid managed hook handler at ${label}: invalid timeout What it means
validateHookHandler allows an optional 'timeout' on a hook handler but requires it to be a non-negative number. A timeout that is a string ("30"), negative, or otherwise non-numeric fails this check, preventing invalid hook config from being written into Claude settings.
Solutions
- Change the timeout at the labeled location to a non-negative number (e.g. 5000, not "5000").
- Remove the timeout field entirely if you want the default behavior.
- Ensure any config generator emits numeric timeouts.
Example fix
// before
{ "type": "command", "command": "node h.js", "timeout": "5000" }
// after
{ "type": "command", "command": "node h.js", "timeout": 5000 } Defensive patterns
Strategy: type-guard
Validate before calling
const hasValidTimeout = (h) => h.timeout === undefined || (typeof h.timeout === 'number' && h.timeout >= 0);
for (const handlers of Object.values(settings.hooks ?? {})) {
for (const h of handlers) if (!hasValidTimeout(h)) throw new Error('timeout must be a non-negative number');
} Type guard
const isValidTimeout = (t) => t === undefined || (typeof t === 'number' && t >= 0);
Try / catch
try {
validateManagedHooks(hooks);
} catch (e) {
if (e.message.includes('invalid timeout')) {
console.error('Coerce timeout to a non-negative number at:', e.message.match(/at (.+?):/)?.[1]);
} else throw e;
} Prevention
- Always write timeouts as unquoted numbers in settings.json.
- Validate generated configs numerically before install (typeof check).
- Omit the timeout field when unsure — it is optional.
When it happens
Trigger: validateManagedHooks processes a handler where hook.timeout is defined but fails typeof hook.timeout === 'number' && hook.timeout >= 0 — e.g. "timeout": "5000" (string) or "timeout": -1.
Common situations: Hand-editing settings and quoting the timeout value; copying configs where timeouts are expressed in seconds as strings; a negative value from a misconfigured generator.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Canonical session snapshot must be an object
- Canonical session snapshot requires
- Canonical session snapshot requires
- Canonical session snapshot requires
- Invalid . : expected an array
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/30d0fea00ac22b21.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:79
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;
}
if (hook.async !== undefined) {
throw new Error(`Invalid managed hook handler at ${label}: async requires command type`);View on GitHub (pinned to 8321021c54)