{"record":{"id":"3d5db4973a983617","repo":"paperclipai/paperclip","slug":"native-adopted-runner-exited","errorCode":"native_adopted_runner_exited","errorMessage":"native_adopted_runner_exited: runner exited before PRP authentication","messagePattern":"native_adopted_runner_exited: runner exited before PRP authentication","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":834,"sourceCode":"    );\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();\n      if (input.activeConnectionCount() === 1) return;\n      const alive = await input.isAlive();\n      if (cancelled) return;\n      input.throwIfFailed();\n      checkDeadline();\n      if (!alive) {\n        throw new Error(\n          \"native_adopted_runner_exited: runner exited before PRP authentication\",\n        );\n      }\n      if (input.activeConnectionCount() === 1) return;\n      await new Promise<void>((resolveWait) => {\n        pollTimer = setTimeout(\n          resolveWait,\n          Math.min(25, deadline - Date.now()),\n        );\n      });\n    }\n  };\n  try {\n    await Promise.race([\n      observe(),\n      input.failure,\n      new Promise<never>((_resolve, reject) => {\n        deadlineTimer = setTimeout(","sourceCodeStart":816,"sourceCodeEnd":852,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L816-L852","documentation":"While waiting for an adopted runner to authenticate, the wait loop polls whether the runner process is still alive. If the process has exited before PRP authentication completed, waiting longer is pointless and the durable session must be preserved for operator recovery, so the loop throws this coded error naming the exit-before-auth condition.","triggerScenarios":"Calling awaitAdoptedRunnerAuthentication (via #awaitAdoptedRunnerConnection) when isAlive() returns false during the polling loop — i.e., the adopted runner process died after adoption started but before it opened its single authenticated connection.","commonSituations":"The adopted CLI process crashed on startup (bad config, missing binary, version mismatch); the process was killed externally (OOM, supervisor); authentication hung long enough that the process was terminated; incompatible runner/protocol versions causing an immediate exit.","solutions":["Check the runner process logs/stderr to determine why it exited before authentication","Verify the runner binary exists, is executable, and matches the expected protocol version","Restart/re-adopt the runner process, preserving its durable session state for recovery","Increase runnerReconnectGraceMs only if the process is being killed by a timeout race, not if it is crashing"],"exampleFix":"// before\nawait awaitAdoptedRunnerAuthentication({ isAlive, ... }); // throws raw exit error\n// after\ntry {\n  await awaitAdoptedRunnerAuthentication({ isAlive, ... });\n} catch (err) {\n  if ((err as Error).message.startsWith(\"native_adopted_runner_exited\")) {\n    await recoverRunnerSession(); // inspect logs, restart runner, preserve durable session\n  } else throw err;\n}","handlingStrategy":"try-catch","validationCode":"if (!(await isAlive())) {\n  throw new Error(\"runner already dead; restart it before awaiting PRP authentication\");\n}","typeGuard":"const runnerReadyForAuthWait = async (isAlive: () => Promise<boolean>) =>\n  (await isAlive()) && Number.isSafeInteger(timeoutMs) && timeoutMs > 0;","tryCatchPattern":"try {\n  await awaitAdoptedRunnerAuthentication({ ... });\n} catch (err) {\n  if ((err as Error).message.startsWith(\"native_adopted_runner_exited\")) {\n    await collectRunnerLogsAndRecover();\n  } else throw err;\n}","preventionTips":["Verify the runner binary/version before adoption","Capture the runner's stderr to diagnose early exits","Use a supervisor that restarts crashed runners and preserves durable session state"],"tags":["process","authentication","timeout"],"backgroundTag":"process-exited-before-ready","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}