{"record":{"id":"4f1bc021d087208f","repo":"stablyai/orca","slug":"agent-session-ownership-unknown","errorCode":null,"errorMessage":"agent_session_ownership_unknown","messagePattern":"agent_session_ownership_unknown","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/daemon/daemon-pty-adapter.ts","lineNumber":1505,"sourceCode":"        ],\n        missingNamedPipe ? 'windows_named_pipe_missing' : undefined\n      )\n      throw error\n    }\n  }\n\n  private validatedAgentSessionOwners(\n    owners: unknown\n  ): { agentSessionOwners: AgentSessionOwnerBinding[] } | Record<string, never> {\n    if (owners === undefined) {\n      return {}\n    }\n    if (\n      !Array.isArray(owners) ||\n      owners.length > MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES ||\n      !owners.every((owner) => isAgentSessionOwnerBinding(owner) && owner.phase === 'live')\n    ) {\n      throw new Error('agent_session_ownership_unknown')\n    }\n    return owners.length > 0\n      ? { agentSessionOwners: owners.map(cloneAgentSessionOwnerBinding) }\n      : {}\n  }\n\n  // Why: the Manage Sessions panel needs the full SessionInfo (pid, state,\n  // createdAt) per session for display; listProcesses drops that detail for\n  // the IPtyProvider contract. Keep both in parallel rather than widening\n  // the provider surface.\n  async listSessions(): Promise<SessionInfo[]> {\n    await this.ensureConnected()\n    const result = await this.client.request<ListSessionsResult>('listSessions', undefined)\n    return result.sessions\n      .filter((s) => s.isAlive)\n      .map((session) => ({\n        ...session,\n        ...this.validatedAgentSessionOwners(session.agentSessionOwners)","sourceCodeStart":1487,"sourceCodeEnd":1523,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/daemon/daemon-pty-adapter.ts#L1487-L1523","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Update both daemon and client to a coherent build so the AgentSessionOwnerBinding schema matches.","If you control the daemon, ensure it only emits phase === 'live' bindings within the entry cap.","Treat the throw as a trust-boundary failure: do not retry with a relaxed validator; surface 'session ownership unreadable' to the UI."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES } from '../../shared/claimed-agent-pty-owner'\nimport { isAgentSessionOwnerBinding } from '...'\n\nfunction isValidOwners(owners: unknown): owners is AgentSessionOwnerBinding[] {\n  return (\n    Array.isArray(owners) &&\n    owners.length <= MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES &&\n    owners.every((o) => isAgentSessionOwnerBinding(o) && o.phase === 'live')\n  )\n}","typeGuard":"function isValidOwners(owners: unknown): owners is AgentSessionOwnerBinding[] {\n  return (\n    Array.isArray(owners) &&\n    owners.length <= MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES &&\n    owners.every((o) => isAgentSessionOwnerBinding(o) && o.phase === 'live')\n  )\n}","tryCatchPattern":"try {\n  const sessions = await adapter.listSessions()\n} catch (e) {\n  if (e instanceof Error && e.message === 'agent_session_ownership_unknown') {\n    // trust-boundary failure; show 'session ownership unreadable', do NOT retry with relaxed validation\n  } else throw e\n}","preventionTips":["Ship daemon and client from a coherent build so the AgentSessionOwnerBinding schema matches.","Treat this as a trust-boundary failure — never relax the validator to recover.","Cap agent-session ownership claims upstream so the wire payload stays well under 1024 entries."],"tags":["daemon","pty","agent-session","validation","trust-boundary","wire"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}