stablyai/orca · error · Error
Daemon no longer owns its endpoint; reconnect
Error message
Daemon no longer owns its endpoint; reconnect
What it means
DAEMON_ENDPOINT_LOST_MESSAGE: the daemon detected it no longer owns the canonical socket endpoint (hasLostEndpointOwnership() returned true), meaning a replacement daemon has published over its path. A non-attach createOrAttach is refused because any session created on a lost endpoint would be unreachable by clients, producing terminals that accept keystrokes but never run. Attach to an already-hosted session is intentionally still allowed so the retiring daemon can drain.
Source
Thrown at src/main/daemon/daemon-server.ts:988
throw new Error('Daemon temporarily unavailable; reconnect')
}
if (!client?.authenticatedPairEstablished || client.streamSocket === null) {
// Why: a control-only replacement can't own terminal admission or erase the prior client's retirement request.
throw new Error('Daemon client connection is incomplete; reconnect')
}
const p = request.payload
const attachOnly = p.attachOnly === true
// Why check here and not only on the watchdog: publishing cannot be made atomic against
// a publisher preempted between proving an entry dead and replacing it, so this daemon
// can lose the endpoint at any moment. The watchdog notices within a poll, which is far
// too late if a session was accepted in between — that session is then reachable by
// nobody, and the user sees a terminal that acknowledges input and never runs it.
// Why creation only: an attach reaches a session this daemon already hosts, over a
// connection that already exists. Refusing that would break the drain a retiring daemon
// depends on, and it strands nothing — the session is already here.
if (!attachOnly && this.hasLostEndpointOwnership()) {
this.requestRetirementForLostEndpoint()
throw new Error(DAEMON_ENDPOINT_LOST_MESSAGE)
}
this.createOrAttachInFlight++
let routedSessionId = p.sessionId
let result: Awaited<ReturnType<TerminalHost['createOrAttach']>>
try {
if (
p.agentSessionEnsure !== undefined &&
(!isAgentSessionExecutionClaim(p.agentSessionEnsure.claim) ||
!isAgentSessionSurfaceBinding(p.agentSessionEnsure.surface))
) {
throw new Error('agent_session_identity_required')
}
if (!attachOnly) {
await this.preparePtySpawnUnlessCanceled(p.sessionId, clientId)
}
if (p.historySeed !== undefined && p.historySeedTransferId !== undefined) {
throw new Error('Multiple terminal history seed sources')
}View on GitHub (pinned to 1136503c6a)
Solutions
- Treat this as a reconnect signal: the client should discover and connect to the replacement daemon that now owns the endpoint, then retry createOrAttach there.
- On the host side, let the retiring daemon finish draining its existing sessions (it allows attach-only) before forcing it down.
- Avoid launching concurrent daemons that bind the same canonical socket path; the ownership protocol is designed for sequential handover, not parallel publishers.
- If this fires repeatedly, check that the publish-endpoint ownership protocol (link/rename/verify) is not being bypassed by a custom launcher.
Example fix
// before: retry createOrAttach on the same (superseded) daemon
try {
await daemon.rpc('createOrAttach', payload)
} catch (e) { /* ignore */ }
// after: on endpoint-lost, rediscover the live daemon first
try {
await daemon.rpc('createOrAttach', payload)
} catch (e) {
if (isDaemonGoneError(e)) {
daemon = await discoverDaemonEndpoint()
await daemon.rpc('createOrAttach', payload)
}
} Defensive patterns
Strategy: retry
Type guard
// Match the daemon client's detection of an endpoint-lost condition
function isDaemonEndpointLost(e: unknown): boolean {
return e instanceof Error && e.message === 'Daemon no longer owns its endpoint; reconnect'
} Try / catch
try {
await daemon.rpc('createOrAttach', payload)
} catch (e) {
if (isDaemonEndpointLost(e)) {
daemon = await discoverAndConnectDaemon()
await daemon.rpc('createOrAttach', payload)
} else { throw e }
} Prevention
- Always rediscover the canonical endpoint and reconnect when this message arrives; the old daemon is retiring.
- Do not launch parallel daemons against the same canonical socket path.
- Respect attach-only draining on a retiring daemon rather than forcing new creates onto it.
When it happens
Trigger: A non-attach 'createOrAttach' (attachOnly !== true) issued to a daemon that has been superseded at its canonical socket path by a newer publisher. The replacement won the link/rename ownership protocol, so this daemon is retiring.
Common situations: Two daemon launches racing during app restart or version upgrade; a watchdog relaunching the daemon while the old one still hosts sessions; a developer manually starting a second daemon instance that takes over the endpoint.
Related errors
- Daemon endpoint unavailable: ${reason}
- Daemon PTY "${id}" is awaiting recovery
- Daemon client connection is incomplete; reconnect
- Daemon temporarily unavailable; reconnect
- [plain-node-entry-guard] "${entryName}" reaches chunk "${chu
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/5ea9e0c98a1d21ed.
Report an issue: GitHub.