{"record":{"id":"00c845340d1d4c34","repo":"paperclipai/paperclip","slug":"native-runner-warm-transition-completion-pending","errorCode":"native_runner_warm_transition_completion_pending","errorMessage":"native_runner_warm_transition_completion_pending","messagePattern":"native_runner_warm_transition_completion_pending","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":3290,"sourceCode":"      diagnostics: [\"lab transport selected authenticated durable PRP\"],\n    };\n  }\n\n  evidence(): CapabilityRunnerdProcessEvidence {\n    return structuredClone(this.#evidence);\n  }\n\n  async request(\n    method: string,\n    params: Record<string, unknown>,\n  ): Promise<Record<string, unknown>> {\n    if (this.#closed) throw new Error(\"PRP Codex transport is closed\");\n    this.#throwIfFailed();\n    if (\n      this.#pendingWarmRecoveryCompletion !== null &&\n      ![\"thread/read\", \"initialize\", \"collaborationMode/list\"].includes(method)\n    ) {\n      throw new Error(\"native_runner_warm_transition_completion_pending\");\n    }\n    if (method === \"initialize\") return { user: {} };\n    if (method === \"thread/start\") return this.#start(params);\n    if (method === \"collaborationMode/list\") {\n      // runnerd negotiates the real Codex preset or the provider-proxy-owned\n      // planning contract during session.open. This transport-level mask\n      // confirms that closed boundary; turn/start remains runner-managed and\n      // never forwards this sentinel to the outer TypeScript driver.\n      return this.options.provider === undefined ||\n        this.options.provider === \"codex\" ||\n        this.options.provider === \"opencode\" ||\n        this.options.provider === \"acpx\"\n        ? {\n            data: [\n              {\n                name: \"Plan\",\n                mode: \"plan\",\n                model: \"runner-managed\",","sourceCodeStart":3272,"sourceCodeEnd":3308,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L3272-L3308","documentation":"During a warm recovery/transition the transport buffers which methods are safe (thread/read, initialize, collaborationMode/list); any other JSON-RPC method is rejected while #pendingWarmRecoveryCompletion is non-null. This prevents requests from racing the in-progress recovery that re-binds runner state.","triggerScenarios":"Calling request() with any method other than thread/read, initialize, or collaborationMode/list (e.g. thread/start, turn/step) while a warm recovery completion is still pending.","commonSituations":"Client reconnect issuing turn requests immediately after warm attach; automation firing turn/start before recovery finalizes; a long recovery overlapping normal traffic.","solutions":["Wait for warm-recovery completion before sending turn/start or other non-read methods (poll thread/read or listen for the completion signal).","Restrict the reconnection handshake to initialize / thread/read / collaborationMode/list until recovery completes.","Retry the rejected method with backoff after the pending completion clears.","If recovery appears stuck, investigate the recovery path rather than forcing requests."],"exampleFix":"// before\nawait transport.request('thread/turns/start', params); // may throw during recovery\n// after\nawait transport.waitForWarmRecoveryCompletion();\nawait transport.request('thread/turns/start', params);","handlingStrategy":"retry","validationCode":"// no direct predicate exposed; probe with an allowed method first\nawait transport.request('thread/read', { threadId }); // permitted during warm recovery","typeGuard":null,"tryCatchPattern":"try {\n  await transport.request('thread/turns/start', params);\n} catch (err) {\n  if ((err as Error).message === 'native_runner_warm_transition_completion_pending') {\n    await waitForWarmRecovery(transport); // poll thread/read or completion signal\n    await transport.request('thread/turns/start', params);\n  } else throw err;\n}","preventionTips":["Serialize reconnection: finish handshake (initialize/thread/read) before turn calls","Back off and retry turn methods on this error","Surface warm-recovery state in UI so clients wait"],"tags":["lifecycle","recovery","state-transition","runner"],"backgroundTag":"invalid-state-transition","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"}