stablyai/orca · error
relay pairing client closed
Error message
relay pairing client closed
What it means
Thrown in the returned client's sendRequest when, after awaiting authenticatedPromise, closed || !authenticated is true. The socket has been torn down (via close(), a transport error, or onclose) or never authenticated, so no further RPCs can be sent.
Source
Thrown at mobile/src/transport/mobile-relay-physical-client.ts:166
log('info', 'Relay: pairing socket closed', cellHost)
} else {
log('warn', 'Relay: pairing socket closed', pairingRelayErrorDetail(error))
}
channel.dispose()
rejectAuthenticated(error)
for (const request of pending.values()) {
clearTimeout(request.timer)
request.reject(error)
}
pending.clear()
socket.close()
}
return {
async sendRequest(method, params) {
await authenticatedPromise
if (closed || !authenticated) {
throw new Error('relay pairing client closed')
}
const id = `relay-pair-${++requestCounter}`
return new Promise<RpcResponse>((resolve, reject) => {
const timer = setTimeout(() => {
pending.delete(id)
reject(new Error(`relay pairing RPC timed out: ${method}`))
}, requestTimeoutMs)
pending.set(id, { resolve, reject, timer })
if (
!channel.sendText(JSON.stringify({ id, deviceToken: args.deviceToken, method, params }))
) {
clearTimeout(timer)
pending.delete(id)
reject(new Error('relay E2EE channel not ready'))
}
})
},
close: () => {View on GitHub (pinned to 1136503c6a)
Solutions
- Await the previous request and handle its rejection before sending again.
- Treat this error as terminal for the client; create a new one via connectMobileRelayForPairing to reconnect.
- Surface a disconnected state in the UI and disable further send attempts.
Example fix
// before
const resp = await client.sendRequest('install')
// ...later, after a close/error:
await client.sendRequest('resume-confirm') // throws 'relay pairing client closed'
// after
try {
await client.sendRequest('install')
} catch {
client = connectMobileRelayForPairing(args) // new client for retry
await client.sendRequest('install')
} Defensive patterns
Strategy: try-catch
Validate before calling
// The client does not expose `closed`; track it yourself alongside the handle.
let clientClosed = false
const client = connectMobileRelayForPairing(args)
const wrappedSend = async (method: string, params?: unknown) => {
if (clientClosed) throw new Error('caller-side guard: client already closed')
try {
return await client.sendRequest(method, params)
} catch (error) {
clientClosed = true
throw error
}
} Try / catch
try {
await client.sendRequest('install')
} catch (error) {
if (error instanceof Error && error.message === 'relay pairing client closed') {
// terminal: build a new client via connectMobileRelayForPairing to retry
}
} Prevention
- Await each request and handle its rejection before issuing the next so you observe the terminal close.
- Treat 'relay pairing client closed' as terminal; rebuild the client to reconnect.
- Disable send in the UI when a prior request failed with a connection error.
When it happens
Trigger: Calling sendRequest after close(), after a prior transport error/onclose triggered fail(), or while a previously awaited request was rejecting due to disconnect.
Common situations: The caller does not observe the rejection of a prior request and issues another; racing a request with a user-initiated close; a network drop mid-RPC.
Related errors
- relay pairing RPC unavailable after relay path authenticatio
- desktop returned no relay endpoint after credential install
- relay credential install result does not match pairing journ
- ${response.error.code}: ${response.error.message}
- mobile relay pairing journal identity mismatch
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/065b511778a17d37.
Report an issue: GitHub.