{"record":{"id":"9355652078edf34c","repo":"paperclipai/paperclip","slug":"api-key-and-api-key-env-are-mutually-exclusive","errorCode":null,"errorMessage":"--api-key and --api-key-env are mutually exclusive.","messagePattern":"--api-key and --api-key-env are mutually exclusive\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/test-drive.ts","lineNumber":228,"sourceCode":"        \"DATABASE_MIGRATION_URL from the selected data directory's .env, or choose a fresh --data-dir.\",\n    );\n  }\n\n  const config = readConfigFile(configPath);\n  if (config?.database.mode === \"postgres\") {\n    throw new Error(\n      \"test-drive cannot reuse a data directory configured for an external PostgreSQL database. \" +\n        \"Choose a fresh data directory or change database.mode to embedded-postgres.\",\n    );\n  }\n}\n\nexport function resolveTestDriveBootstrap(\n  options: TestDriveOptions,\n  env: NodeJS.ProcessEnv = process.env,\n): ResolvedTestDriveBootstrap {\n  if (options.apiKey !== undefined && options.apiKeyEnv !== undefined) {\n    throw new Error(\"--api-key and --api-key-env are mutually exclusive.\");\n  }\n\n  const harness = options.harness ?? \"claude\";\n  const definition = HARNESS_DEFINITIONS[harness];\n  if (!definition) {\n    throw new Error(`Unsupported test-drive harness: ${String(harness)}.`);\n  }\n\n  const companyName = (options.companyName ?? \"Test Company\").trim();\n  const agentName = (options.agentName ?? \"CEO\").trim();\n  if (!companyName) throw new Error(\"--company-name cannot be empty.\");\n  if (!agentName) throw new Error(\"--agent-name cannot be empty.\");\n\n  const model = options.model;\n  if (model !== undefined && (!model || model.trim() !== model)) {\n    throw new Error(\"--model cannot be empty or have surrounding whitespace.\");\n  }\n  if (","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/cli/src/commands/test-drive.ts#L210-L246","documentation":"After a warm transition, the transport polls the runner store until the recovered identity matches the desired identity and the warm transition completes. If the identity has not converged within the grace period (`runnerReconnectGraceMs`, default 5s), or the runner process exited while waiting, it throws this error indicating recovery never finished in time.","triggerScenarios":"The wait loop at the transition point times out (`Date.now() >= deadline`) or `#runnerHasExited()` observes the runnerd process died before `recoveryIdentityMatches` became true and `warmTransition` cleared.","commonSituations":"Slow or overloaded machine where the runner cannot recover within the default 5s grace; runner crash mid-transition (OOM, bad binary); setting `runnerReconnectGraceMs` too low for large session state restores.","solutions":["Increase `runnerReconnectGraceMs` to allow slower warm recovery to finish (e.g. 30_000).","Inspect runnerd logs for a crash during the transition and fix the underlying exit (memory, binary path, config).","Retry the warm transition once the runner is healthy, or fall back to a cold start."],"exampleFix":"// before\nnew RunnerdCodexTransport({ runnerReconnectGraceMs: 5_000 });\n// after\nnew RunnerdCodexTransport({ runnerReconnectGraceMs: 30_000 });","handlingStrategy":"retry","validationCode":"const graceMs = options.runnerReconnectGraceMs ?? 5_000;\nif (graceMs < 10_000) console.warn(\"warm transition grace period may be too short on slow hosts\");","typeGuard":"null","tryCatchPattern":"try {\n  await transport.startTurn(input);\n} catch (e) {\n  if (e.message === \"native_runner_warm_transition_recovery_pending\" && runnerAlive()) {\n    await retryWithBackoff(() => transport.startTurn(input), { retries: 2 });\n  }\n}","preventionTips":["Set runnerReconnectGraceMs generously (30s+) on slow or heavily loaded hosts.","Monitor runnerd liveness so crashes are distinguished from slow recovery.","Retry warm transitions with backoff before falling back to cold start."],"tags":["runner","warm-transition","timeout"],"backgroundTag":"request-timeout","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"}