stablyai/orca · error

invalid relay hello

Error message

invalid relay hello

What it means

Thrown when RelayPhoneHelloSchema.safeParse(value) fails: the first frame parsed as JSON but does not match the relay-hello union (type literal 'relay-hello', ok flag, credentialKind 'invite'|'resume', leaseExpiresAt, and for resume the acceptedCredentialVersion/acceptedAs/resumeExpiresAt fields).

Source

Thrown at mobile/src/transport/mobile-relay-e2ee-link.ts:118

        .catch((error: unknown) => this.fail(asError(error)))
    }
    this.socket.onerror = () => this.fail(new Error('relay transport error'))
    this.socket.onclose = (event) => this.fail(new RelayOuterError(event.code || 1006))
  }

  private acceptHello(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 !== this.options.expectedCredentialKind) {
      throw new Error('relay credential resolved as an unexpected credential kind')
    }
    this.outerReady = true
    this.options.onHello?.(parsed.data)
    this.channel.start()
  }

  private fail(error: Error): void {
    if (this.closed) {
      return
    }
    this.closed = true
    this.channel.dispose()

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Capture parsed.error.issues from a local safeParse to see which field failed.
  2. If the frame is relay-moved, re-resolve the endpoint via the resume director before reconnecting.
  3. Align client and relay-cell versions so the hello schema matches.
  4. Verify cellUrl and relayHostId target the intended cell.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate a captured frame against the same schema the cell should use
import { RelayPhoneHelloSchema } from '../../../src/shared/mobile-relay-phone-protocol'
const ok = RelayPhoneHelloSchema.safeParse(JSON.parse(capturedFrame)).success

Type guard

import { RelayPhoneHelloSchema, type RelayPhoneHello } from '../../../src/shared/mobile-relay-phone-protocol'
function isRelayHello(value: unknown): value is RelayPhoneHello {
  return RelayPhoneHelloSchema.safeParse(value).success
}

Try / catch

try {
  const link = new MobileRelayE2eeLink(options)
} catch (error) {
  if (error instanceof Error && error.message === 'invalid relay hello') {
    // re-resolve endpoint via director in case the cell sent relay-moved
  }
}

Prevention

When it happens

Trigger: The cell sent valid JSON that is not a relay-hello: a 'relay-moved' notice (host reassigned to a new cellUrl), an error object, or a hello missing/adding required fields under the .strict() schema.

Common situations: Client/cell version skew where a newer cell changed the hello shape; the host was reassigned and the cell advertises relay-moved instead of relay-hello; the endpoint URL reached a different relay service that speaks another protocol.

Related errors


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