stablyai/orca · warning · Error

relay endpoint unavailable for direct pairing upgrade

Error message

relay endpoint unavailable for direct pairing upgrade

What it means

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.

Source

Thrown at mobile/src/transport/mobile-relay-direct-upgrade.ts:75

    ...args.dependencies
  }
  let journal = await dependencies.readJournal(args.host.id)
  if (!journal) {
    journal = createMobileRelayDirectUpgradeJournal(args.host.id, dependencies.randomBytes)
    // Why: the stable reqId and pending secret must survive a lost install response.
    await dependencies.writeJournal(journal)
  }

  const initial = await getEndpoints(args.client, journal.reqId)
  if (initial === 'method-not-found') {
    await dependencies.clearJournal(args.host.id)
    return null
  }
  if (initial.installStatus?.state === 'committed') {
    return publishCommitted(args.host, journal, initial, dependencies)
  }
  if (!initial.relay) {
    throw new Error('relay endpoint unavailable for direct pairing upgrade')
  }

  const provisionResponse = await args.client.sendRequest('pairing.provisionRelay', {
    reqId: journal.reqId,
    newResumeTokenHash: journal.pendingResumeTokenHash
  })
  if (isMethodNotFound(provisionResponse)) {
    await dependencies.clearJournal(args.host.id)
    return null
  }
  const installed = DeviceCredentialInstalledSchema.parse(requireSuccess(provisionResponse))
  assertDirectInstall(journal, installed)
  const reconciled = await getEndpoints(args.client, journal.reqId)
  if (reconciled === 'method-not-found') {
    throw new Error('relay endpoint reconciliation became unavailable')
  }
  assertCommitted(reconciled, installed)
  return publishCommitted(args.host, journal, reconciled, dependencies)

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Treat this as 'no upgrade available right now' rather than a hard failure — defer and retry later.
  2. Confirm the desktop has relay enabled and that the host profile is current.
  3. Fall back to cloud/relay pairing if direct upgrade is unavailable.

Example fix

// before
const result = await upgradeDirectMobileRelay({ client, host }) // throws

// after
let result
try { result = await upgradeDirectMobileRelay({ client, host }) }
catch (e) {
  if (e.message === 'relay endpoint unavailable for direct pairing upgrade') {
    scheduleRetryIn(60_000); result = null
  } else throw e
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check endpoints before attempting upgrade:
const initial = await getEndpoints(client, journal.reqId).catch(() => null)
if (!initial?.relay) { /* defer upgrade */ return null }

Type guard

function hasRelay(e: { relay?: unknown } | null): boolean { return !!e?.relay }

Try / catch

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 }

Prevention

When it happens

Trigger: 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`.

Common situations: 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.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/821c9921c8a270c2. Report an issue: GitHub.