stablyai/orca · warning · PtyWriteUnavailableError

Daemon PTY "${id}" is awaiting recovery

Error message

Daemon PTY "${id}" is awaiting recovery

What it means

DaemonPtyAdapter.write rejects synchronously with PtyWriteUnavailableError when the session is 'recoverable' (active, respawn possible, adoption not closed) AND either the session is already flagged as awaiting recovery or the daemon client is currently disconnected. The intent is to drive the renderer pane to remount on a connection that can actually come back, rather than silently dropping keystrokes.

Source

Thrown at src/main/daemon/daemon-pty-adapter.ts:999

      return null
    }
  }

  write(id: string, data: string): void {
    this.markSessionDirty(id)
    // Why recoverable and not just active: rejecting a write asks the pane to remount,
    // which only helps if this endpoint can come back. A legacy adapter has no respawn,
    // so its reattach fails and the pane rebuilds empty — losing scrollback the user
    // could still read. Keep the pre-existing silent drop for those.
    const recoverable =
      this.activeSessionIds.has(id) && !this.respawnAdoptionClosed && Boolean(this.respawnFn)
    if (
      recoverable &&
      (this.sessionsAwaitingDaemonRecovery.has(id) || !this.client.isConnected())
    ) {
      this.sessionsAwaitingDaemonRecovery.add(id)
      this.reconnectAfterWriteFailure()
      throw new PtyWriteUnavailableError(`Daemon PTY "${id}" is awaiting recovery`)
    }
    const delivered = this.client.notify('write', { sessionId: id, data })
    if (!delivered && recoverable) {
      this.sessionsAwaitingDaemonRecovery.add(id)
      this.reconnectAfterWriteFailure()
      throw new PtyWriteUnavailableError(`Daemon PTY "${id}" is awaiting recovery`)
    }
  }

  resize(id: string, cols: number, rows: number): void {
    this.markSessionDirty(id)
    this.client.notify('resize', { sessionId: id, cols, rows })
  }

  pauseProducer(id: string): void {
    if (!this.supportsProducerFlowControl) {
      return
    }

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Catch PtyWriteUnavailableError at the pane layer and remount/retry once the adapter reports reconnection (sessionsAwaitingDaemonRecovery clears).
  2. Do not infinitely retry in a tight loop — let reconnectAfterWriteFailure drive reconnection and re-issue on the recovered signal.
  3. If this fires repeatedly, inspect the daemon client connection state (isConnected) and the respawn reason.

Example fix

// before
try { adapter.write(sessionId, data) } catch { /* swallow */ }

// after
import { isPtyWriteUnavailableError } from '../providers/pty-write-unavailable-error'
try {
  adapter.write(sessionId, data)
} catch (e) {
  if (isPtyWriteUnavailableError(e)) {
    // queue input, await reconnect, then drain the queue
  } else throw e
}
Defensive patterns

Strategy: try-catch

Type guard

import { isPtyWriteUnavailableError } from '../providers/pty-write-unavailable-error'

// narrows unknown -> PtyWriteUnavailableError
function isRecoverablePtyWrite(e: unknown): boolean {
  return isPtyWriteUnavailableError(e)
}

Try / catch

import { isPtyWriteUnavailableError } from '../providers/pty-write-unavailable-error'

try {
  adapter.write(sessionId, data)
} catch (e) {
  if (isPtyWriteUnavailableError(e)) {
    // queue input, await reconnect (sessionsAwaitingDaemonRecovery clears), then drain
  } else {
    throw e
  }
}

Prevention

When it happens

Trigger: User typing into a terminal whose daemon connection just dropped; a second write arriving on a session already in sessionsAwaitingDaemonRecovery; client.notify path not yet re-established after a transient disconnect while respawnFn is configured.

Common situations: Daemon restart, network blip to a remote daemon, machine sleep/resume dropping the socket, slow reconnect after a daemon crash where the user kept typing.

Related errors


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