stablyai/orca · error · Error

agent_session_ownership_unknown

Error message

agent_session_ownership_unknown

What it means

validatedAgentSessionOwners guards the listSessions wire payload. It throws a plain Error('agent_session_ownership_unknown') when agentSessionOwners is present but not an array, exceeds MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES (1024), or any element fails isAgentSessionOwnerBinding or has a phase other than 'live'. This stops a malformed/forged ownership payload from poisoning the Manage Sessions panel.

Source

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

        ],
        missingNamedPipe ? 'windows_named_pipe_missing' : undefined
      )
      throw error
    }
  }

  private validatedAgentSessionOwners(
    owners: unknown
  ): { agentSessionOwners: AgentSessionOwnerBinding[] } | Record<string, never> {
    if (owners === undefined) {
      return {}
    }
    if (
      !Array.isArray(owners) ||
      owners.length > MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES ||
      !owners.every((owner) => isAgentSessionOwnerBinding(owner) && owner.phase === 'live')
    ) {
      throw new Error('agent_session_ownership_unknown')
    }
    return owners.length > 0
      ? { agentSessionOwners: owners.map(cloneAgentSessionOwnerBinding) }
      : {}
  }

  // Why: the Manage Sessions panel needs the full SessionInfo (pid, state,
  // createdAt) per session for display; listProcesses drops that detail for
  // the IPtyProvider contract. Keep both in parallel rather than widening
  // the provider surface.
  async listSessions(): Promise<SessionInfo[]> {
    await this.ensureConnected()
    const result = await this.client.request<ListSessionsResult>('listSessions', undefined)
    return result.sessions
      .filter((s) => s.isAlive)
      .map((session) => ({
        ...session,
        ...this.validatedAgentSessionOwners(session.agentSessionOwners)

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Update both daemon and client to a coherent build so the AgentSessionOwnerBinding schema matches.
  2. If you control the daemon, ensure it only emits phase === 'live' bindings within the entry cap.
  3. Treat the throw as a trust-boundary failure: do not retry with a relaxed validator; surface 'session ownership unreadable' to the UI.
Defensive patterns

Strategy: try-catch

Validate before calling

import { MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES } from '../../shared/claimed-agent-pty-owner'
import { isAgentSessionOwnerBinding } from '...'

function isValidOwners(owners: unknown): owners is AgentSessionOwnerBinding[] {
  return (
    Array.isArray(owners) &&
    owners.length <= MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES &&
    owners.every((o) => isAgentSessionOwnerBinding(o) && o.phase === 'live')
  )
}

Type guard

function isValidOwners(owners: unknown): owners is AgentSessionOwnerBinding[] {
  return (
    Array.isArray(owners) &&
    owners.length <= MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES &&
    owners.every((o) => isAgentSessionOwnerBinding(o) && o.phase === 'live')
  )
}

Try / catch

try {
  const sessions = await adapter.listSessions()
} catch (e) {
  if (e instanceof Error && e.message === 'agent_session_ownership_unknown') {
    // trust-boundary failure; show 'session ownership unreadable', do NOT retry with relaxed validation
  } else throw e
}

Prevention

When it happens

Trigger: A remote daemon (or a tampered/partially-upgraded one) sends agentSessionOwners that is not a LiveAgentSessionOwner[]; a future schema migration where phase gains a new value the local build does not know; an attacker/buzz client forging oversized ownership claims.

Common situations: Mixed-version daemon/client where the daemon publishes a new phase or shape the client does not recognize; compromised or buggy daemon sending garbage; >1024 agent sessions claimed against a single PTY (resource exhaustion / bug).

Related errors


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