{"record":{"id":"065b511778a17d37","repo":"stablyai/orca","slug":"relay-pairing-client-closed","errorCode":null,"errorMessage":"relay pairing client closed","messagePattern":"relay pairing client closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-relay-physical-client.ts","lineNumber":166,"sourceCode":"      log('info', 'Relay: pairing socket closed', cellHost)\n    } else {\n      log('warn', 'Relay: pairing socket closed', pairingRelayErrorDetail(error))\n    }\n    channel.dispose()\n    rejectAuthenticated(error)\n    for (const request of pending.values()) {\n      clearTimeout(request.timer)\n      request.reject(error)\n    }\n    pending.clear()\n    socket.close()\n  }\n\n  return {\n    async sendRequest(method, params) {\n      await authenticatedPromise\n      if (closed || !authenticated) {\n        throw new Error('relay pairing client closed')\n      }\n      const id = `relay-pair-${++requestCounter}`\n      return new Promise<RpcResponse>((resolve, reject) => {\n        const timer = setTimeout(() => {\n          pending.delete(id)\n          reject(new Error(`relay pairing RPC timed out: ${method}`))\n        }, requestTimeoutMs)\n        pending.set(id, { resolve, reject, timer })\n        if (\n          !channel.sendText(JSON.stringify({ id, deviceToken: args.deviceToken, method, params }))\n        ) {\n          clearTimeout(timer)\n          pending.delete(id)\n          reject(new Error('relay E2EE channel not ready'))\n        }\n      })\n    },\n    close: () => {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-relay-physical-client.ts#L148-L184","documentation":"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.","triggerScenarios":"Calling sendRequest after close(), after a prior transport error/onclose triggered fail(), or while a previously awaited request was rejecting due to disconnect.","commonSituations":"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.","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."],"exampleFix":"// before\nconst resp = await client.sendRequest('install')\n// ...later, after a close/error:\nawait client.sendRequest('resume-confirm') // throws 'relay pairing client closed'\n// after\ntry {\n  await client.sendRequest('install')\n} catch {\n  client = connectMobileRelayForPairing(args) // new client for retry\n  await client.sendRequest('install')\n}","handlingStrategy":"try-catch","validationCode":"// The client does not expose `closed`; track it yourself alongside the handle.\nlet clientClosed = false\nconst client = connectMobileRelayForPairing(args)\nconst wrappedSend = async (method: string, params?: unknown) => {\n  if (clientClosed) throw new Error('caller-side guard: client already closed')\n  try {\n    return await client.sendRequest(method, params)\n  } catch (error) {\n    clientClosed = true\n    throw error\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await client.sendRequest('install')\n} catch (error) {\n  if (error instanceof Error && error.message === 'relay pairing client closed') {\n    // terminal: build a new client via connectMobileRelayForPairing to retry\n  }\n}","preventionTips":["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."],"tags":["relay","pairing","lifecycle","rpc"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}