{"record":{"id":"124a5257c99fc0ac","repo":"paperclipai/paperclip","slug":"capability-live-turn-admission-was-abandoned-durin","errorCode":null,"errorMessage":"Capability live turn admission was abandoned during teardown","messagePattern":"Capability live turn admission was abandoned during teardown","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/live-session.ts","lineNumber":1523,"sourceCode":"          await admission.transport.attachRun(binding);\n          this.#providerRunBinding = binding;\n          await this.#persist();\n        } catch (error) {\n          admissionFailure = error;\n        }\n      }\n      // A transport without cross-run attachment keeps subsequent provider\n      // turns inside this capability session's existing governed run. This is\n      // distinct from reusing the provider under a different PRP authority.\n    }\n    let workspaceBefore: PaperclipWorkspaceSnapshot | null = null;\n    try {\n      workspaceBefore = await workspaceBeforePromise;\n    } catch (error) {\n      admissionFailure ??= error;\n    }\n    if (this.#pendingTurnAdmission !== admission) {\n      throw new Error(\"Capability live turn admission was abandoned during teardown\");\n    }\n    if (admissionFailure !== null) {\n      return this.#failTurnAdmission(admission, userEntryId, admissionFailure);\n    }\n    if (admission.cancellation !== null) {\n      return this.#completeInterruptedAdmission(admission, userEntryId);\n    }\n    if (workspaceBefore === null) {\n      this.#appendEvidence(\"diagnostic\", null, {\n        code: \"workspace_baseline_deadline_exceeded\",\n        message:\n          \"The runner could not establish an exact pre-turn workspace baseline inside its bounded admission window; workspace diff projection is unavailable for this turn.\",\n      });\n    }\n    if (\n      this.#pendingTurnAdmission !== admission ||\n      this.#transport !== admission.transport ||\n      this.#status !== \"running\"","sourceCodeStart":1505,"sourceCodeEnd":1541,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/live-session.ts#L1505-L1541","documentation":"During sendMessage turn admission, the session checks that the pending admission object is still the session's current one (#pendingTurnAdmission === admission). If a concurrent teardown (close/fail/interrupt) already released or replaced the admission, the in-flight start can no longer proceed and throws instead of writing into a torn-down session.","triggerScenarios":"Session close/fail/teardown runs while sendMessage is awaiting the workspaceBefore promise (pre-turn workspace snapshot); admission released between start of admission and the equality check.","commonSituations":"A supervisor closes the session concurrently with a user sending a message; a timeout path tears down the session while a turn is starting; error in another awaited step triggered session teardown mid-admission.","solutions":["Ensure session teardown does not run while sendMessage is in flight (serialize lifecycle and turn calls)","Catch this error in the sendMessage caller and treat it as 'turn did not start' — do not retry on the same (torn-down) session","Re-check session status before sending; avoid firing sends after calling close/fail","If teardown is intentional, surface this as an expected cancellation rather than a bug"],"exampleFix":"// before\nconst sendPromise = session.sendMessage(msg);\nsession.close(); // teardown races admission\nawait sendPromise; // throws 'abandoned during teardown'\n// after\nawait session.sendMessage(msg);\nawait session.close();","handlingStrategy":"try-catch","validationCode":"if (session.status === 'closed' || session.status === 'failed') throw new Error('session tearing down; not sending');","typeGuard":"null","tryCatchPattern":"try {\n  await session.sendMessage(msg);\n} catch (e) {\n  if ((e as Error).message === 'Capability live turn admission was abandoned during teardown') {\n    // turn never started; do not retry on this session\n  } else throw e;\n}","preventionTips":["Never call close/fail concurrently with sendMessage; coordinate via a busy flag","Await turn completion before teardown in supervisors and timeout handlers","Treat this error as cancellation, not a transport failure, in retry logic"],"tags":["concurrency","race-condition","lifecycle"],"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"}