{"record":{"id":"821c9921c8a270c2","repo":"stablyai/orca","slug":"relay-endpoint-unavailable-for-direct-pairing-upgr","errorCode":null,"errorMessage":"relay endpoint unavailable for direct pairing upgrade","messagePattern":"relay endpoint unavailable for direct pairing upgrade","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"mobile/src/transport/mobile-relay-direct-upgrade.ts","lineNumber":75,"sourceCode":"    ...args.dependencies\n  }\n  let journal = await dependencies.readJournal(args.host.id)\n  if (!journal) {\n    journal = createMobileRelayDirectUpgradeJournal(args.host.id, dependencies.randomBytes)\n    // Why: the stable reqId and pending secret must survive a lost install response.\n    await dependencies.writeJournal(journal)\n  }\n\n  const initial = await getEndpoints(args.client, journal.reqId)\n  if (initial === 'method-not-found') {\n    await dependencies.clearJournal(args.host.id)\n    return null\n  }\n  if (initial.installStatus?.state === 'committed') {\n    return publishCommitted(args.host, journal, initial, dependencies)\n  }\n  if (!initial.relay) {\n    throw new Error('relay endpoint unavailable for direct pairing upgrade')\n  }\n\n  const provisionResponse = await args.client.sendRequest('pairing.provisionRelay', {\n    reqId: journal.reqId,\n    newResumeTokenHash: journal.pendingResumeTokenHash\n  })\n  if (isMethodNotFound(provisionResponse)) {\n    await dependencies.clearJournal(args.host.id)\n    return null\n  }\n  const installed = DeviceCredentialInstalledSchema.parse(requireSuccess(provisionResponse))\n  assertDirectInstall(journal, installed)\n  const reconciled = await getEndpoints(args.client, journal.reqId)\n  if (reconciled === 'method-not-found') {\n    throw new Error('relay endpoint reconciliation became unavailable')\n  }\n  assertCommitted(reconciled, installed)\n  return publishCommitted(args.host, journal, reconciled, dependencies)","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-relay-direct-upgrade.ts#L57-L93","documentation":"Thrown in `upgradeDirectMobileRelay` when the initial `getEndpoints` call returns a result with no `relay` field and the install is not already committed. The desktop must advertise a relay endpoint for the host before direct upgrade can proceed; without one, there is nowhere to publish the upgraded credential.","triggerScenarios":"Desktop does not have a relay endpoint configured for this host; the host is not yet eligible for relay (e.g. desktop feature flag off); desktop version returns `getEndpoints` but with `relay: null`.","commonSituations":"Desktop build without relay support enabled; host that was never assigned a relay cell; race where the host profile is stale relative to the desktop's current config.","solutions":["Treat this as 'no upgrade available right now' rather than a hard failure — defer and retry later.","Confirm the desktop has relay enabled and that the host profile is current.","Fall back to cloud/relay pairing if direct upgrade is unavailable."],"exampleFix":"// before\nconst result = await upgradeDirectMobileRelay({ client, host }) // throws\n\n// after\nlet result\ntry { result = await upgradeDirectMobileRelay({ client, host }) }\ncatch (e) {\n  if (e.message === 'relay endpoint unavailable for direct pairing upgrade') {\n    scheduleRetryIn(60_000); result = null\n  } else throw e\n}","handlingStrategy":"fallback","validationCode":"// Pre-check endpoints before attempting upgrade:\nconst initial = await getEndpoints(client, journal.reqId).catch(() => null)\nif (!initial?.relay) { /* defer upgrade */ return null }","typeGuard":"function hasRelay(e: { relay?: unknown } | null): boolean { return !!e?.relay }","tryCatchPattern":"try { return await upgradeDirectMobileRelay({ client, host }) } catch (e) { if (e.message === 'relay endpoint unavailable for direct pairing upgrade') { scheduleRetry(60_000); return null } throw e }","preventionTips":["Treat a missing relay endpoint as a soft 'not now', not a fatal error.","Retry direct upgrade periodically when the desktop advertises relay.","Confirm desktop relay feature flag is on."],"tags":["relay","upgrade","feature-flag","endpoints"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}