stablyai/orca · error

invalid relay hello JSON

Error message

invalid relay hello JSON

What it means

Thrown in connectMobileRelayForPairing's acceptRelayHello when the first WS frame is a string but JSON.parse throws. The cell's plaintext hello is not valid JSON during the pairing dial.

Solutions

  1. Log the redacted raw first frame to identify the source.
  2. Confirm cellUrl resolves to a relay cell, not an error/landing page.
  3. Redial after confirming network connectivity.
  4. Verify cell health and version.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const client = connectMobileRelayForPairing(args)
} catch (error) {
  if (error instanceof Error && error.message === 'invalid relay hello JSON') {
    // log redacted raw frame; verify cellUrl; redial on a clean network
  }
}

Prevention

When it happens

Trigger: The cell returned an HTML error page, a plain-text error string, or a partial frame as the first message while the client dials for pairing.

Common situations: cellUrl pointing at a load-balancer/error page; a captive portal interstitial; the cell redeployed mid-dial and emitted a non-JSON banner.

Related errors


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

Appendix: source

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

          acceptRelayHello(event.data)
          return
        }
        await channel.handleMessage(event.data)
      })
      .catch((error: unknown) => fail(asError(error)))
  }
  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) {

View on GitHub (pinned to 1136503c6a)