stablyai/orca · error · RelayOuterError

relay_outer_${code}

Error message

relay_outer_${code}

What it means

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.

Source

Thrown at mobile/src/transport/mobile-relay-physical-client.ts:132

  socket.onerror = () => fail(new Error('relay transport error'))
  socket.onclose = (event) => fail(new RelayOuterError(event.code || 1006))

  function acceptRelayHello(raw: unknown): void {
    if (typeof raw !== 'string') {
      throw new Error('expected plaintext relay hello')
    }
    let value: unknown
    try {
      value = JSON.parse(raw)
    } catch {
      throw new Error('invalid relay hello JSON')
    }
    const parsed = RelayPhoneHelloSchema.safeParse(value)
    if (!parsed.success) {
      throw new Error('invalid relay hello')
    }
    if (!parsed.data.ok) {
      throw new RelayOuterError(parsed.data.code)
    }
    if (parsed.data.credentialKind !== (args.expectedCredentialKind ?? 'invite')) {
      throw new Error('relay credential resolved as an unexpected credential kind')
    }
    outerReady = true
    log('info', 'Relay: cell accepted credential', 'Starting E2EE handshake')
    channel.start()
  }

  function fail(error: Error): void {
    if (closed) {
      return
    }
    closed = true
    if (intentionallyClosed) {
      log('info', 'Relay: pairing socket closed', cellHost)
    } else {
      log('warn', 'Relay: pairing socket closed', pairingRelayErrorDetail(error))

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Decode the 4xxx code per the relay protocol and map it to user guidance.
  2. Refresh the invite from the desktop host for expired/used invites.
  3. For resume failures, fall back to a fresh pair.
  4. Check leaseExpiresAt / resumeExpiresAt to catch clock skew.
Defensive patterns

Strategy: try-catch

Type guard

import { RelayOuterError } from './mobile-relay-e2ee-link'
function isRelayOuterError(error: unknown): error is RelayOuterError {
  return error instanceof RelayOuterError
}

Try / catch

import { RelayOuterError } from './mobile-relay-e2ee-link'
try {
  const client = connectMobileRelayForPairing(args)
} catch (error) {
  if (error instanceof RelayOuterError) {
    // map error.code to UX: refresh invite, re-pair, or report revoked
  }
}

Prevention

When it happens

Trigger: Expired or already-used invite, unknown/revoked resume token, or a credential whose version was superseded during the pairing dial.

Common situations: Invite expired before pairing completed; resume token stale after host rotation; replaying a consumed invite; clock skew making a lease look expired.

Related errors


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