paperclipai/paperclip · error

stop the active turn before reconnecting the Codex transport

Error message

stop the active turn before reconnecting the Codex transport

What it means

This error is thrown by LiveSession.reconnect() in the Codex live-session transport. reconnect() tears down and re-establishes the transport, which is only safe when no turn is in flight: an active turn (#activeTurnId), a pending turn waiter, or a turn still awaiting admission would be orphaned by the disconnect. The library throws this error to prevent silently aborting an in-progress turn during reconnection.

Source

Thrown at packages/paperclip-runner/src/live/live-session.ts:2167

    });
    await this.#persist();
    return this.sendMessage(JSON.stringify({
      schema: "paperclip.semantic-interaction-result.v1",
      interactionId: input.interactionId,
      kind: interaction.kind,
      outcome: input.outcome,
      result: input.result,
      instruction: "Continue using this typed interaction result in the same mock issue session.",
    }));
  }

  async reconnect(): Promise<void> {
    if (
      this.#activeTurnId !== null ||
      this.#turnWaiter !== null ||
      this.#pendingTurnAdmission !== null
    ) {
      throw new Error("stop the active turn before reconnecting the Codex transport");
    }
    await this.#disconnect("reconnect");
    await this.#connect(true);
  }

  async suspend(reason = "session suspended"): Promise<void> {
    if (this.#status === "closed" || this.#status === "suspended") return;
    this.#clearIdleTimer();
    const admission = this.#pendingTurnAdmission;
    if (admission !== null) {
      admission.cancellation = { reason, resumeLifecycle: false };
      this.#status = "suspending";
      await this.#persist();
      // Closing first actively aborts an attach/start RPC whose response was
      // lost. Waiting before close would let lifecycle shutdown deadlock on
      // the very transport operation that teardown must terminate.
      await this.#disconnect(reason);
      await this.#boundAdmissionSettlementAfterClose(admission, reason);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Wait for the active turn to complete (or call the stop/cancel-turn API) before invoking reconnect()
  2. Check session turn state (active turn id / waiter) before scheduling a reconnect
  3. Serialize reconnect attempts behind a mutex or queue so they cannot overlap turn submission
  4. If the turn is wedged, stop/abort the turn and wait for its terminal event, then reconnect

Example fix

// before
await session.reconnect();
// after
if (session.hasActiveTurn) {
  await session.stopTurn(); // or await session.waitForTurnCompletion()
}
await session.reconnect();
Defensive patterns

Strategy: try-catch

Validate before calling

function canReconnect(session) {
  return !session.hasActiveTurn && !session.isWaitingForTurn && !session.hasPendingTurnAdmission;
}

Type guard

function isIdle(session: LiveSession): boolean {
  return session.activeTurnId === null;
}

Try / catch

try {
  await session.reconnect();
} catch (err) {
  if (err.message.includes("stop the active turn before reconnecting")) {
    await session.stopTurn();
    await session.reconnect();
  } else throw err;
}

Prevention

When it happens

Trigger: Calling session.reconnect() while a turn is executing, a caller is awaiting turn completion (#turnWaiter set), or a turn is queued awaiting admission (#pendingTurnAdmission set).

Common situations: App-level watchdog logic that reconnects on flaky network timers without first checking turn state; racing a reconnect against a submitTurn; reusing a session object after a turn timed out client-side while the session still considers it active.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/2bcd9d05aba13fa0. Report an issue: GitHub.