{"record":{"id":"e4602678ddeb370f","repo":"stablyai/orca","slug":"relay-outer-code-e46026","errorCode":null,"errorMessage":"relay_outer_${code}","messagePattern":"relay_outer_(.+?)","errorType":"exception","errorClass":"RelayOuterError","httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-relay-physical-client.ts","lineNumber":132,"sourceCode":"  socket.onerror = () => fail(new Error('relay transport error'))\n  socket.onclose = (event) => fail(new RelayOuterError(event.code || 1006))\n\n  function acceptRelayHello(raw: unknown): void {\n    if (typeof raw !== 'string') {\n      throw new Error('expected plaintext relay hello')\n    }\n    let value: unknown\n    try {\n      value = JSON.parse(raw)\n    } catch {\n      throw new Error('invalid relay hello JSON')\n    }\n    const parsed = RelayPhoneHelloSchema.safeParse(value)\n    if (!parsed.success) {\n      throw new Error('invalid relay hello')\n    }\n    if (!parsed.data.ok) {\n      throw new RelayOuterError(parsed.data.code)\n    }\n    if (parsed.data.credentialKind !== (args.expectedCredentialKind ?? 'invite')) {\n      throw new Error('relay credential resolved as an unexpected credential kind')\n    }\n    outerReady = true\n    log('info', 'Relay: cell accepted credential', 'Starting E2EE handshake')\n    channel.start()\n  }\n\n  function fail(error: Error): void {\n    if (closed) {\n      return\n    }\n    closed = true\n    if (intentionallyClosed) {\n      log('info', 'Relay: pairing socket closed', cellHost)\n    } else {\n      log('warn', 'Relay: pairing socket closed', pairingRelayErrorDetail(error))","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-relay-physical-client.ts#L114-L150","documentation":"RelayOuterError is thrown (message relay_outer_${code}) when the cell hello has ok:false with a code in [4000,4999] during the pairing dial. The cell rejected the pairing credential supplied in the relay-auth frame.","triggerScenarios":"Expired or already-used invite, unknown/revoked resume token, or a credential whose version was superseded during the pairing dial.","commonSituations":"Invite expired before pairing completed; resume token stale after host rotation; replaying a consumed invite; clock skew making a lease look expired.","solutions":["Decode the 4xxx code per the relay protocol and map it to user guidance.","Refresh the invite from the desktop host for expired/used invites.","For resume failures, fall back to a fresh pair.","Check leaseExpiresAt / resumeExpiresAt to catch clock skew."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import { RelayOuterError } from './mobile-relay-e2ee-link'\nfunction isRelayOuterError(error: unknown): error is RelayOuterError {\n  return error instanceof RelayOuterError\n}","tryCatchPattern":"import { RelayOuterError } from './mobile-relay-e2ee-link'\ntry {\n  const client = connectMobileRelayForPairing(args)\n} catch (error) {\n  if (error instanceof RelayOuterError) {\n    // map error.code to UX: refresh invite, re-pair, or report revoked\n  }\n}","preventionTips":["Map every 4xxx RelayOuterError code to a concrete user action.","Check lease/refresh timestamps locally before dialing to pre-empt expiry.","Do not replay a credential after a 4xxx rejection."],"tags":["relay","websocket","authentication","pairing","relay-outer-error"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}