{"record":{"id":"472cedf81c946508","repo":"paperclipai/paperclip","slug":"runnerreconnectgracems-must-be-a-positive-safe-int","errorCode":null,"errorMessage":"runnerReconnectGraceMs must be a positive safe integer","messagePattern":"runnerReconnectGraceMs must be a positive safe integer","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":809,"sourceCode":"  // immediately before force-killing the runner. The suspension proof itself\n  // must retain a finite opportunity to cross the durable command boundary.\n  const suspensionReserveMs = Math.min(2_500, Math.ceil(graceMs / 2));\n  return {\n    preparationDeadline: startedAtMs + graceMs - suspensionReserveMs,\n    closeDeadline: startedAtMs + graceMs,\n  };\n}\n\nasync function awaitAdoptedRunnerAuthentication(input: {\n  activeConnectionCount: () => number;\n  isAlive: () => Promise<boolean> | boolean;\n  throwIfFailed: () => void;\n  failure: Promise<never>;\n  ready?: () => Promise<void>;\n  timeoutMs: number;\n}): Promise<void> {\n  if (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs <= 0) {\n    throw new Error(\"runnerReconnectGraceMs must be a positive safe integer\");\n  }\n  const deadline = Date.now() + input.timeoutMs;\n  const timeoutError = () =>\n    new Error(\n      \"native_adopted_runner_authentication_timeout: the existing runner did not authenticate within \" +\n        `${input.timeoutMs}ms; preserve its process and durable session for operator recovery`,\n    );\n  let cancelled = false;\n  let pollTimer: NodeJS.Timeout | undefined;\n  let deadlineTimer: NodeJS.Timeout | undefined;\n  const checkDeadline = () => {\n    if (Date.now() >= deadline) throw timeoutError();\n  };\n  const observe = async () => {\n    await input.ready?.();\n    while (!cancelled) {\n      input.throwIfFailed();\n      checkDeadline();","sourceCodeStart":791,"sourceCodeEnd":827,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L791-L827","documentation":"awaitAdoptedRunnerAuthentication waits for an adopted (already-running) native runner to authenticate over PRP within a grace period. Before starting the wait it validates timeoutMs (runnerReconnectGraceMs) and requires a positive safe integer; zero, negative, non-integer, NaN, or out-of-safe-range values throw this error immediately rather than producing a broken deadline.","triggerScenarios":"Calling awaitAdoptedRunnerAuthentication (via #awaitAdoptedRunnerConnection) with timeoutMs equal to 0, negative, non-integer (e.g. 1500.5), NaN, Infinity, or a value beyond Number.MAX_SAFE_INTEGER.","commonSituations":"Config value runnerReconnectGraceMs parsed from environment/config as a string or float instead of an integer; unset config defaulting to NaN after Number(undefined); a user setting 0 intending 'no wait'; unit conversions producing fractional milliseconds.","solutions":["Set runnerReconnectGraceMs to a positive integer number of milliseconds (e.g. 30000)","Validate/parse the config value with Number.isSafeInteger before passing it in","Fix the config source so the value is parsed as a number, not a string or undefined","Choose a grace period large enough for the runner to authenticate (not 0 or negative)"],"exampleFix":"// before\nawaitAdoptedRunnerAuthentication({ timeoutMs: Number(process.env.RUNNER_RECONNECT_GRACE_MS) }); // NaN/0 possible\n// after\nconst graceMs = Number.parseInt(process.env.RUNNER_RECONNECT_GRACE_MS ?? \"30000\", 10);\nif (!Number.isSafeInteger(graceMs) || graceMs <= 0) throw new Error(\"invalid runnerReconnectGraceMs\");\nawaitAdoptedRunnerAuthentication({ timeoutMs: graceMs });","handlingStrategy":"validation","validationCode":"const graceMs = config.runnerReconnectGraceMs;\nif (!Number.isSafeInteger(graceMs) || graceMs <= 0) {\n  throw new Error(`runnerReconnectGraceMs must be a positive safe integer, got ${graceMs}`);\n}","typeGuard":"const isValidGraceMs = (v: unknown): v is number =>\n  typeof v === \"number\" && Number.isSafeInteger(v) && v > 0;","tryCatchPattern":null,"preventionTips":["Parse grace-period config with Number.parseInt and validate before use","Default to a sane positive value when config is absent","Reject 0/negative/float values at config-load time, not at call time"],"tags":["validation","config","timeout"],"backgroundTag":"invalid-argument-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}