affaan-m/ECC · error
Invalid managed hook handler at
Error message
Invalid managed hook handler at ${label}: invalid command What it means
validateHookHandler validates each managed hook entry written into Claude settings.json. For a hook of type 'command', the 'command' field must be either a non-empty string or a non-empty array of non-empty strings. This error is thrown when hook.command is missing, empty, an empty array, or contains non-string/blank entries.
Solutions
- Set hook.command to a non-empty string, e.g. {type:'command', command:'node hooks/my-hook.js'}.
- If using array form, provide a non-empty array of non-empty strings, e.g. command:['node','hooks/my-hook.js'].
- Check the field is literally named 'command' (not 'cmd', 'run', or 'script').
- Run a pre-flight check that every command-type hook has a valid command before calling the install API.
- Trim whitespace-only strings — isNonEmptyString rejects values that are blank after trim.
Example fix
// before
{ "type": "command", "command": "" }
// after
{ "type": "command", "command": "node hooks/hooks.json-lint.js" } Defensive patterns
Strategy: validation
Validate before calling
function isValidCommandHook(h){ return h.type==='command' && (typeof h.command==='string' && h.command.trim()!=='' || Array.isArray(h.command) && h.command.length>0 && h.command.every(c=>typeof c==='string' && c.trim()!=='')); } Type guard
const isCommandHook = (h) => typeof h === 'object' && h !== null && h.type === 'command' && (typeof h.command === 'string' || Array.isArray(h.command));
Try / catch
try { materializeManagedHooks(desired); } catch (e) { if (String(e.message).includes('invalid command')) { console.error('A command hook has a missing/empty command field:', e.message); process.exitCode = 1; } else throw e; } Prevention
- Always pair type:'command' with a non-empty command field.
- Prefer the string form of command for single executables.
- Lint settings.json hooks with a JSON schema in CI.
- Never build command arrays by splitting empty strings.
When it happens
Trigger: Calling install/managed-hook APIs (via validateManagedHooks) with a handler {type:'command'} whose command is undefined, '', ' ', [], [null], [''], or a non-array non-string value like 42 or {cmd:'x'}.
Common situations: Hand-edited settings.json with a typo'd key (cmds instead of command); programmatically generated hooks where the command template resolved to empty; YAML/JSON configs converted where the command was moved to a different field; array-form command built by splitting an empty string producing [].
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Invalid hook entry at
- Invalid hooks config: expected a JSON object with a hooks…
- Invalid . : expected a non-empty hook array
- Invalid : expected at least one hook event
- Invalid managed hook handler at
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/b50659739e3a5d51.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:88
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`);
}
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'))View on GitHub (pinned to 8321021c54)