{"record":{"id":"30d0fea00ac22b21","repo":"affaan-m/ECC","slug":"invalid-managed-hook-handler-at-label-invalid-timeout","errorCode":null,"errorMessage":"Invalid managed hook handler at ${label}: invalid timeout","messagePattern":"Invalid managed hook handler at (.+?): invalid timeout","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings.js","lineNumber":79,"sourceCode":"  const pathsMatch = process.platform === 'win32'\n    ? resolvedDestination.toLowerCase() === resolvedExpected.toLowerCase()\n    : resolvedDestination === resolvedExpected;\n  if (!pathsMatch) {\n    throw new Error(\n      `Refusing to manage Claude hooks outside the canonical settings file: ${destinationPath}`\n    );\n  }\n}\n\nfunction validateHookHandler(hook, label) {\n  if (!isJsonObject(hook)) {\n    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`);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings.js#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{ \"type\": \"command\", \"command\": \"node h.js\", \"timeout\": \"5000\" }\n// after\n{ \"type\": \"command\", \"command\": \"node h.js\", \"timeout\": 5000 }","handlingStrategy":"type-guard","validationCode":"const hasValidTimeout = (h) => h.timeout === undefined || (typeof h.timeout === 'number' && h.timeout >= 0);\nfor (const handlers of Object.values(settings.hooks ?? {})) {\n  for (const h of handlers) if (!hasValidTimeout(h)) throw new Error('timeout must be a non-negative number');\n}","typeGuard":"const isValidTimeout = (t) => t === undefined || (typeof t === 'number' && t >= 0);","tryCatchPattern":"try {\n  validateManagedHooks(hooks);\n} catch (e) {\n  if (e.message.includes('invalid timeout')) {\n    console.error('Coerce timeout to a non-negative number at:', e.message.match(/at (.+?):/)?.[1]);\n  } else throw e;\n}","preventionTips":["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."],"tags":["hooks","validation","type-mismatch","schema"],"backgroundTag":"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"}