stablyai/orca · error · SessionNotFoundError

Session not found: ${sessionId}

Error message

Session not found: ${sessionId}

What it means

SessionNotFoundError from createOrAttachTerminalSession: the session id is in active teardown (sessionTeardown holds it) or the existing session is marked isTerminating. Descendant capture must finish before attach or recreation, so a session being torn down is reported as not found rather than handed out — handing it out could deliver a doomed session to the caller while teardown owns its process.

Source

Thrown at src/main/daemon/terminal-host-session-create.ts:39

  onDeadSessionRemoved: (sessionId: string) => void
  onSessionCreated: (sessionId: string, generation: string | undefined, isAlive: boolean) => void
  onSessionExit: (sessionId: string, generation: string | undefined) => void
}

export async function createOrAttachTerminalSession(
  opts: InternalCreateOrAttachOptions,
  deps: TerminalHostSessionCreateDependencies
): Promise<CreateOrAttachResult> {
  if (deps.creationFenced) {
    throw new Error('Terminal host is shutting down')
  }
  opts.onSessionResolved?.(opts.sessionId)
  const existing = deps.sessions.get(opts.sessionId)

  // Why: descendant capture must finish before attach or recreation, or the
  // caller could receive a doomed session while teardown owns its process.
  if (deps.sessionTeardown.get(opts.sessionId) || existing?.isTerminating) {
    throw new SessionNotFoundError(opts.sessionId)
  }

  if (existing && existing.isAlive && !existing.isTerminating) {
    const snapshot = existing.getSnapshot()
    existing.detachAllClients()
    const token = existing.attachClient(opts.streamClient)
    return {
      isNew: false,
      snapshot,
      pid: existing.pid,
      shellState: existing.shellState,
      incarnationId: existing.incarnationId,
      ...getDaemonSessionResultMetadata(existing),
      attachToken: token
    }
  }

  if (existing?.isAlive && existing.isTerminating) {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Wait for the session's teardown to complete (descendant capture) before retrying createOrAttach.
  2. If attaching, retry after the terminating session is fully reaped.
  3. Avoid issuing createOrAttach for ids known to be in teardown; track teardown state client-side.
  4. If recreation is intended, ensure prior teardown finishes first so a new generation can spawn cleanly.
Defensive patterns

Strategy: retry

Validate before calling

// Check teardown state before createOrAttach to avoid the not-found round-trip
if (deps.sessionTeardown.get(sessionId) || existing?.isTerminating) {
  await waitForTeardown(sessionId)
}

Type guard

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

Try / catch

try {
  return await createOrAttachTerminalSession(opts, deps)
} catch (e) {
  if (e instanceof SessionNotFoundError && isSessionInTeardown(sessionId)) {
    await waitForTeardownComplete(sessionId)
    return await createOrAttachTerminalSession(opts, deps)
  }
  throw e
}

Prevention

When it happens

Trigger: createOrAttachTerminalSession where deps.sessionTeardown.get(opts.sessionId) is truthy or existing?.isTerminating is true, hit before the live-attach or terminate-specific branches.

Common situations: A createOrAttach for a sessionId that is concurrently being killed/teardown; an attach racing session exit; recreating a session whose teardown has not finished capturing descendants.

Related errors


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