stablyai/orca · info · TerminalAttachCanceledError

Attach canceled for session ${sessionId}

Error message

Attach canceled for session ${sessionId}

What it means

preparePtySpawnUnlessCanceled registers a per-session preparation record before the async preparePtySpawn() probe, so a concurrent close can flip preparation.canceled before a subprocess is created. If canceled is true after the probe resolves, it throws TerminalAttachCanceledError(sessionId) — the create was aborted to avoid spawning a PTY the user already closed.

Source

Thrown at src/main/daemon/daemon-server.ts:902

    timer.unref()
    socket.once('close', start)
    socket.once('error', start)
    this.pendingShutdownReplies.set(key, { start })
  }

  private async preparePtySpawnUnlessCanceled(sessionId: string, clientId: string): Promise<void> {
    const preparation: PendingPtySpawnPreparation = {
      canceled: false,
      clientId
    }
    const pending = this.pendingPtySpawnPreparations.get(sessionId) ?? new Set()
    pending.add(preparation)
    this.pendingPtySpawnPreparations.set(sessionId, pending)
    try {
      // Why: register before the async probe so a concurrent close can cancel this creation before a subprocess exists.
      await this.preparePtySpawn()
      if (preparation.canceled) {
        throw new TerminalAttachCanceledError(sessionId)
      }
    } finally {
      pending.delete(preparation)
      if (pending.size === 0) {
        this.pendingPtySpawnPreparations.delete(sessionId)
      }
    }
  }

  private cancelPendingPtySpawnPreparations(sessionId: string): boolean {
    const pending = this.pendingPtySpawnPreparations.get(sessionId)
    if (!pending) {
      return false
    }
    for (const preparation of pending) {
      preparation.canceled = true
    }
    return true

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Treat TerminalAttachCanceledError as benign — the session was closed by the user/caller before spawn; do not surface as a crash.
  2. Avoid issuing createOrAttach for a sessionId the UI has already torn down; track in-flight tabs.
  3. If it fires constantly, audit the renderer for open/close thrash on the same id.
Defensive patterns

Strategy: try-catch

Type guard

import { TerminalAttachCanceledError } from './daemon-errors'

function isAttachCanceled(e: unknown): e is TerminalAttachCanceledError {
  return e instanceof TerminalAttachCanceledError
}

Try / catch

try {
  await host.createOrAttach({ sessionId, attachOnly: false, ... })
} catch (e) {
  if (isAttachCanceled(e)) {
    // benign — closed before spawn; suppress
  } else throw e
}

Prevention

When it happens

Trigger: createOrAttach (attachOnly=false) running while the renderer closes the same session id; close arrives between probe registration and the spawn; rapid open/close thrash on a terminal tab.

Common situations: User opens then immediately closes a terminal; a reconnect race that reissues create then cancels; UI remount churn driving attach/cancel cycles.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/40d1800baecdd1f1. Report an issue: GitHub.