{"record":{"id":"f0eff37ba11ccbac","repo":"paperclipai/paperclip","slug":"capability-live-session-is-not-connected","errorCode":null,"errorMessage":"Capability live session is not connected","messagePattern":"Capability live session is not connected","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/live-session.ts","lineNumber":1466,"sourceCode":"    attempt.failureCode = status === \"failed\"\n      ? requireNonEmpty(failureCode ?? \"attempt_failed\", \"attempt_failure_code\")\n      : null;\n    await this.#persist();\n    return this.snapshot();\n  }\n\n  async sendMessage(\n    message: string,\n    /** Launch-only diagnostics may opt out; qualification campaigns must not. */\n    options: { allowMissingUsage?: boolean } = {},\n  ): Promise<CapabilityLiveTurnResult> {\n    const value = message.trim();\n    if (value.length === 0) throw new Error(\"Capability live messages cannot be empty\");\n    if (this.#status === \"suspended\" || this.#transport === null) {\n      await this.#connect(true);\n    }\n    if (this.#transport === null || this.#status === \"closed\" || this.#status === \"failed\") {\n      throw new Error(\"Capability live session is not connected\");\n    }\n    if (\n      this.#activeTurnId !== null ||\n      this.#turnWaiter !== null ||\n      this.#pendingTurnAdmission !== null\n    ) {\n      throw new Error(\"Capability live session already has an active turn\");\n    }\n    let settleAdmission!: () => void;\n    const admission: PendingTurnAdmission = {\n      transport: this.#transport,\n      cancellation: null,\n      settled: new Promise<void>((resolveSettled) => {\n        settleAdmission = resolveSettled;\n      }),\n      settle: () => settleAdmission(),\n    };\n    this.#pendingTurnAdmission = admission;","sourceCodeStart":1448,"sourceCodeEnd":1484,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/live-session.ts#L1448-L1484","documentation":"After attempting an automatic reconnect, sendMessage verifies the session has a live transport and a healthy status. If the transport is still null or the status is 'closed' or 'failed', the session cannot carry a turn, so it throws. This is the session-level 'not connected' guard.","triggerScenarios":"Calling sendMessage on a session whose transport dropped and whose #connect(true) reconnect failed; calling after the session was explicitly closed or entered 'failed' status (e.g. Codex process exited).","commonSituations":"Codex agent process crashed; network drop that reconnect could not recover; caller holds a stale session reference after close; suspended session whose resume also failed.","solutions":["Recreate the session (or call the session's connect/resume path) and retry sendMessage","Check session.status and transport presence before sending; handle closed/failed as terminal","Inspect why #connect failed (process spawn error, auth, network) via the session's error/failure events","Replace any cached session reference that was closed with a freshly constructed one"],"exampleFix":"// before\nawait session.sendMessage(msg); // session already closed\n// after\nif (session.status === 'closed' || session.status === 'failed') {\n  session = await createLiveSession(opts);\n}\nawait session.sendMessage(msg);","handlingStrategy":"try-catch","validationCode":"if (session.status === 'closed' || session.status === 'failed' || session.transport === null) {\n  throw new Error('session not live; reconnect first');\n}","typeGuard":"function isLive(session: { status: string; transport: unknown }): boolean {\n  return session.transport !== null && session.status !== 'closed' && session.status !== 'failed';\n}","tryCatchPattern":"try {\n  await session.sendMessage(msg);\n} catch (e) {\n  if ((e as Error).message === 'Capability live session is not connected') {\n    await session.connect(); // or recreate session\n    await session.sendMessage(msg);\n  } else throw e;\n}","preventionTips":["Subscribe to session status/failure events and stop sending when closed/failed","Wrap sends in a reconnect-with-backoff helper","Avoid caching session references across long idle periods"],"tags":["connection","lifecycle","websocket"],"backgroundTag":"connection-refused","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"}