{"record":{"id":"dcaf47c72a4af267","repo":"stablyai/orca","slug":"agent-session-identity-required","errorCode":null,"errorMessage":"agent_session_identity_required","messagePattern":"agent_session_identity_required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/daemon/daemon-server.ts","lineNumber":999,"sourceCode":"        // too late if a session was accepted in between — that session is then reachable by\n        // nobody, and the user sees a terminal that acknowledges input and never runs it.\n        // Why creation only: an attach reaches a session this daemon already hosts, over a\n        // connection that already exists. Refusing that would break the drain a retiring daemon\n        // depends on, and it strands nothing — the session is already here.\n        if (!attachOnly && this.hasLostEndpointOwnership()) {\n          this.requestRetirementForLostEndpoint()\n          throw new Error(DAEMON_ENDPOINT_LOST_MESSAGE)\n        }\n        this.createOrAttachInFlight++\n        let routedSessionId = p.sessionId\n        let result: Awaited<ReturnType<TerminalHost['createOrAttach']>>\n        try {\n          if (\n            p.agentSessionEnsure !== undefined &&\n            (!isAgentSessionExecutionClaim(p.agentSessionEnsure.claim) ||\n              !isAgentSessionSurfaceBinding(p.agentSessionEnsure.surface))\n          ) {\n            throw new Error('agent_session_identity_required')\n          }\n          if (!attachOnly) {\n            await this.preparePtySpawnUnlessCanceled(p.sessionId, clientId)\n          }\n          if (p.historySeed !== undefined && p.historySeedTransferId !== undefined) {\n            throw new Error('Multiple terminal history seed sources')\n          }\n          const historySeedChunks =\n            p.historySeedTransferId !== undefined\n              ? this.historySeedTransfers.take(clientId, p.historySeedTransferId)\n              : p.historySeed !== undefined\n                ? [p.historySeed]\n                : undefined\n          result = await this.host.createOrAttach({\n            sessionId: p.sessionId,\n            cols: p.cols,\n            rows: p.rows,\n            cwd: p.cwd,","sourceCodeStart":981,"sourceCodeEnd":1017,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/daemon/daemon-server.ts#L981-L1017","documentation":"When createOrAttach carries an agentSessionEnsure payload, both its 'claim' and 'surface' fields must pass strict shape validation (isAgentSessionExecutionClaim and isAgentSessionSurfaceBinding). The claim requires a correct digestVersion, base64url digests of exact length, and a resumable agent; the surface requires a valid tabId, leafId, and a terminalHandle prefixed 'term_'. If either fails, the daemon refuses to bind an agent session identity, since a malformed claim cannot be trusted for session ownership.","triggerScenarios":"createOrAttach with p.agentSessionEnsure present where claim.digestVersion is wrong, identityDigest/worktreeScopeDigest are not 43-char base64url SHA-256, keyId contains non-base64url chars, surface.tabId/leafId/terminalHandle do not match their formats, or the agent field is not a recognized resumable TuiAgent.","commonSituations":"A client sending a stale agentSessionEnsure shape from an older protocol version (digestVersion mismatch); hand-constructed or truncated digest fields; a surface binding whose terminalHandle lacks the 'term_' prefix; cross-version client/host where the claim format changed.","solutions":["Re-derive the agentSessionEnsure on the client using the host's authority helpers so claim and surface match the current protocol version.","Validate the payload with isAgentSessionExecutionClaim() and isAgentSessionSurfaceBinding() locally before sending createOrAttach.","If you do not need an agent session claim, omit agentSessionEnsure entirely rather than sending a placeholder.","On version mismatch, negotiate protocol version in hello and construct the claim in the negotiated shape."],"exampleFix":"// before: send an unchecked agentSessionEnsure\nawait daemon.rpc('createOrAttach', { ..., agentSessionEnsure: raw })\n\n// after: validate before sending, omit if invalid\nimport { isAgentSessionExecutionClaim, isAgentSessionSurfaceBinding } from '../../shared/agent-session-host-authority'\nif (raw && isAgentSessionExecutionClaim(raw.claim) && isAgentSessionSurfaceBinding(raw.surface)) {\n  await daemon.rpc('createOrAttach', { ..., agentSessionEnsure: raw })\n} else {\n  await daemon.rpc('createOrAttach', { ... })\n}","handlingStrategy":"validation","validationCode":"import { isAgentSessionExecutionClaim, isAgentSessionSurfaceBinding } from '../../shared/agent-session-host-authority'\nfunction agentSessionEnsureIsValid(e: { claim: unknown; surface: unknown } | undefined): boolean {\n  if (e === undefined) return true\n  return isAgentSessionExecutionClaim(e.claim) && isAgentSessionSurfaceBinding(e.surface)\n}","typeGuard":"import { isAgentSessionExecutionClaim, isAgentSessionSurfaceBinding, type AgentSessionExecutionClaim, type AgentSessionSurfaceBinding } from '../../shared/agent-session-host-authority'\nfunction isAgentSessionEnsure(v: unknown): v is { claim: AgentSessionExecutionClaim; surface: AgentSessionSurfaceBinding } {\n  if (typeof v !== 'object' || v === null) return false\n  const o = v as { claim?: unknown; surface?: unknown }\n  return isAgentSessionExecutionClaim(o.claim) && isAgentSessionSurfaceBinding(o.surface)\n}","tryCatchPattern":"try {\n  await daemon.rpc('createOrAttach', { ..., agentSessionEnsure })\n} catch (e) {\n  if (e instanceof Error && e.message === 'agent_session_identity_required') {\n    // drop the claim and retry without it, or rebuild it correctly\n    await daemon.rpc('createOrAttach', { ... })\n  } else { throw e }\n}","preventionTips":["Build agentSessionEnsure with the host authority helpers so claim/surface match the current digest version.","Validate locally with the is* guards before sending; omit the field entirely if you do not need a claim.","Re-derive the claim when the protocol version changes (negotiated in hello)."],"tags":["agent-session","validation","protocol","rpc"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}