{"record":{"id":"4d7fa56444153e11","repo":"paperclipai/paperclip","slug":"codex-run-attach-busy","errorCode":"codex_run_attach_busy","errorMessage":"codex_run_attach_busy","messagePattern":"codex_run_attach_busy","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts","lineNumber":91,"sourceCode":"  }\n\n  ids(): ReturnType<HarnessSession[\"ids\"]> {\n    return {\n      driverSessionId: this.opened.threadId,\n      providerSessionId: this.opened.providerSessionId,\n      displayId: this.opened.threadId,\n    };\n  }\n\n  async attachRun(input: { runId: string }): Promise<void> {\n    this.assertProtocolIntegrity();\n    const transportOwnsQuiescence = this.transport.attachRun !== undefined;\n    if (\n      this.turnStartPending ||\n      (!transportOwnsQuiescence &&\n        (this.activeTurnId !== null || this.pendingRuntimeRequestMap.size > 0))\n    ) {\n      throw new Error(\"codex_run_attach_busy\");\n    }\n    if (!input.runId) throw new Error(\"codex_run_attach_invalid\");\n    await this.transport.attachRun?.({\n      runId: input.runId,\n      turnId: `turn_attachment_${randomUUID().replaceAll(\"-\", \"\")}`,\n      itemId: `item_attachment_${randomUUID().replaceAll(\"-\", \"\")}`,\n    });\n    this.assertProtocolIntegrity();\n    if (transportOwnsQuiescence) {\n      // Runnerd's attachment contract performs two durable readiness probes,\n      // drains the settled provider tail, and rotates authority atomically.\n      // Its proof supersedes host reducer state that can remain stale when a\n      // semantic-result consumer stops before the interrupt terminal arrives.\n      // Drop only the prior run's already-proven-settled buffered suffix.\n      this.activeTurnId = null;\n      this.pendingRuntimeRequestMap.clear();\n      this.eventQueue.clear();\n    }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts#L73-L109","documentation":"attachRun on CodexHarnessSession attaches an orchestration run to the session, but only when the session is quiescent. It throws \"codex_run_attach_busy\" when a turn start is already pending, or — when the transport does not own quiescence — when there is still an active turn or pending runtime requests. This is a guard against corrupting an in-flight turn's state by re-attaching mid-flight.","triggerScenarios":"Calling session.attachRun({ runId }) while turnStartPending is true, or (when transport.attachRun is undefined) while activeTurnId is set to an in-flight turn or pendingRuntimeRequestMap still holds unanswered runtime requests.","commonSituations":"Orchestration recovery racing with a still-running turn; double-invoking run attach after a timeout without waiting for the previous turn to settle; attaching to a session whose interrupt or runtime question has not been resolved yet; a stale host-side reducer state that thinks the session is idle while a turn is active.","solutions":["Wait for the current turn to reach a terminal/settled state before calling attachRun (await turn completion or the run's result event).","Resolve or drain pending runtime requests (questions/interrupts) before attaching; if using a transport with attachRun support, ensure quiescence is handled there.","Retry attachRun after the session reports quiescence (activeTurnId === null and pendingRuntimeRequestMap empty); the error is recoverable by design, not fatal.","If state appears stale (no actual work running), inspect eventQueue/terminal state and restart the session to clear optimistic state."],"exampleFix":"// before\nawait session.attachRun({ runId });\n// after\nif (!session.isIdle()) await waitForTurnSettled(session);\nawait session.attachRun({ runId });","handlingStrategy":"retry","validationCode":"function sessionIsQuiescent(state) {\n  return !state.turnStartPending && state.activeTurnId === null &&\n    state.pendingRuntimeRequestCount === 0;\n}\nif (!sessionIsQuiescent(currentState)) {\n  throw new Error(\"attach deferred: session not quiescent\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await session.attachRun({ runId });\n} catch (e) {\n  if (e.message === \"codex_run_attach_busy\") {\n    await waitForTurnSettled(session); // poll events or await turn terminal\n    await session.attachRun({ runId });\n  } else throw e;\n}","preventionTips":["Always await the previous turn's terminal event before attaching a new run.","Serialize attachRun calls through a per-session queue/mutex.","Resolve or time out pending runtime requests (questions) before recovery attach."],"tags":["concurrency","state","codex","attach"],"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-14T05:17:10.506Z"}