{"record":{"id":"33cf97ec174d2a53","repo":"affaan-m/ECC","slug":"invalid-managed-hook-handler-at-label-invalid-async-flag","errorCode":null,"errorMessage":"Invalid managed hook handler at ${label}: invalid async flag","messagePattern":"Invalid managed hook handler at (.+?): invalid async flag","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":91,"sourceCode":"    throw new Error(`Invalid managed hook handler at ${label}: expected a JSON object`);\n  }\n  if (!VALID_HOOK_TYPES.has(hook.type)) {\n    throw new Error(`Invalid managed hook handler at ${label}: unsupported type`);\n  }\n  if (hook.timeout !== undefined && (typeof hook.timeout !== 'number' || hook.timeout < 0)) {\n    throw new Error(`Invalid managed hook handler at ${label}: invalid timeout`);\n  }\n\n  if (hook.type === 'command') {\n    const validCommand = isNonEmptyString(hook.command)\n      || (Array.isArray(hook.command)\n        && hook.command.length > 0\n        && hook.command.every(isNonEmptyString));\n    if (!validCommand) {\n      throw new Error(`Invalid managed hook handler at ${label}: invalid command`);\n    }\n    if (hook.async !== undefined && typeof hook.async !== 'boolean') {\n      throw new Error(`Invalid managed hook handler at ${label}: invalid async flag`);\n    }\n    return;\n  }\n\n  if (hook.async !== undefined) {\n    throw new Error(`Invalid managed hook handler at ${label}: async requires command type`);\n  }\n  if (hook.type === 'http') {\n    if (!isNonEmptyString(hook.url)) {\n      throw new Error(`Invalid managed hook handler at ${label}: invalid url`);\n    }\n    if (\n      hook.headers !== undefined\n      && (!isJsonObject(hook.headers)\n        || !Object.values(hook.headers).every(value => typeof value === 'string'))\n    ) {\n      throw new Error(`Invalid managed hook handler at ${label}: invalid headers`);\n    }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L73-L109","documentation":"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).","triggerScenarios":"validateManagedHooks encounters a command handler like {type:'command', command:'x', async:'true'} or async:1 or async:null.","commonSituations":"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.","solutions":["Change the async value to a real JSON boolean: true or false.","Remove the async field entirely if you do not need it (undefined is accepted).","Fix your config generator to emit booleans, not strings (JSON.stringify the value, don't String() it).","Re-validate the settings object before writing it."],"exampleFix":"// before\n{ \"type\": \"command\", \"command\": \"npm test\", \"async\": \"true\" }\n// after\n{ \"type\": \"command\", \"command\": \"npm test\", \"async\": true }","handlingStrategy":"validation","validationCode":"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));","typeGuard":"const hasValidAsync = (h) => h.async === undefined || typeof h.async === 'boolean';","tryCatchPattern":"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; }","preventionTips":["Write booleans unquoted in JSON/YAML configs.","Round-trip configs through JSON.parse to catch stringified booleans.","Template generators should emit typeof-checked values."],"tags":["config","validation","types"],"backgroundTag":"config-type-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}