stablyai/orca · error · SessionNotFoundError

Session not found: ${sessionId}

Error message

Session not found: ${sessionId}

What it means

SessionNotFoundError is thrown by TerminalHost.getAliveSession when a sessionId resolves to no entry in the sessions Map, or to a session whose isAlive flag is false. It guards every command-targeting method (write, resize, signal, clearScrollback, closeStartupQueryAuthority, inspectProcess, getCwd) so operations cannot be applied to a dead or nonexistent PTY. The class carries a stable name ('SessionNotFoundError') so callers across the daemon protocol boundary can branch on it.

Source

Thrown at src/main/daemon/terminal-host.ts:245

    this.disposePromise = disposePromise
    void disposePromise.catch(() => {
      // Why: keep failed native owners retryable on a later shutdown request.
      if (this.disposePromise === disposePromise) {
        this.disposePromise = null
      }
    })
    return disposePromise
  }

  private async disposeSessions(): Promise<void> {
    await shutdownTerminalHostSessions(this.sessions, this.onFinalCheckpoint)
    this.killedTombstones.clear()
  }

  private getAliveSession(sessionId: string): Session {
    const session = this.sessions.get(sessionId)
    if (!session || !session.isAlive) {
      throw new SessionNotFoundError(sessionId)
    }
    return session
  }
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Catch SessionNotFoundError by name at the client/RPC handler boundary and treat it as a benign session-gone condition (close the pane, stop sending input) rather than surfacing a crash.
  2. Before issuing commands, check host.listSessions() or a cached liveness flag if you suspect the session may have exited.
  3. Ensure the UI tears down pane state on receipt of the session-exit event so no further write/resize calls are dispatched.
  4. If you hold a sessionId long-term, subscribe to its exit notification and null your reference on exit.

Example fix

// before: unguarded write crashes on exited session
host.write(sessionId, keystroke)
// after: tolerate a session that exited between events
try {
  host.write(sessionId, keystroke)
} catch (e) {
  if (e instanceof SessionNotFoundError) {
    onSessionGone(sessionId)
    return
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Cheap pre-check before issuing a command to a possibly-dead session.
import type { SessionInfo } from './types'
function sessionLikelyLive(host: TerminalHost, sessionId: string): boolean {
  return host.listSessions().some((s: SessionInfo) => s.id === sessionId)
}

Type guard

import { SessionNotFoundError } from './types'
function isSessionNotFound(e: unknown): e is SessionNotFoundError {
  return e instanceof SessionNotFoundError
}

Try / catch

try {
  host.write(sessionId, data)
} catch (e) {
  if (e instanceof SessionNotFoundError) {
    onSessionGone(sessionId) // close pane, stop sending
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling host.write(sessionId, data), host.resize, host.signal, host.clearScrollback, host.closeStartupQueryAuthority, host.inspectProcess, or host.getCwd with a sessionId that has already exited (onSessionExit ran and reapSession deleted it) or was never created. Also reachable when a client sends input to a pane whose shell has terminated but the renderer has not yet been notified.

Common situations: A terminal pane whose shell exited but the client still sends keystrokes; a stale sessionId persisted across a daemon restart (the session map is in-memory); a race where the user closes a tab mid-operation and a queued resize/write arrives after reapSession; referencing a sessionId from a different daemon incarnation.

Related errors


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