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]. This is the relay cell explicitly rejecting the connect credential supplied in the relay-auth frame.
Source
Thrown at mobile/src/transport/mobile-relay-e2ee-link.ts:121
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()
this.options.onError(error)
this.socket.close()
}View on GitHub (pinned to 1136503c6a)
Solutions
- Decode the 4xxx code against the relay protocol and map it to user-facing guidance (expired vs revoked vs unknown).
- For expired/used invites, generate a fresh invite from the desktop host and restart the connect.
- For resume failures, fall back to a fresh pair.
- Compare the current time against leaseExpiresAt / resumeExpiresAt from a prior successful hello 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 link = new MobileRelayE2eeLink(options)
} catch (error) {
if (error instanceof RelayOuterError) {
switch (error.code) {
case 4101: // example: expired invite
promptRegenerateInvite()
break
default:
showRelayRejection(error.code)
}
}
} Prevention
- Treat RelayOuterError as the canonical credential-rejection signal and map each 4xxx code to UX.
- Check leaseExpiresAt/resumeExpiresAt locally before dialing to pre-empt expiry codes.
- Never replay a credential after a 4xxx rejection; obtain a fresh one.
When it happens
Trigger: The cell received an invite or resume credential it refuses: expired invite, already-consumed invite, unknown/revoked resume token, credential version superseded after a host reassignment.
Common situations: Invite expired before the user completed pairing; resume token invalidated after host rotation; clock skew making a lease look expired; replaying a credential from a completed/abandoned attempt.
Related errors
- relay_outer_${code}
- E2EE device authentication rejected
- invalid relay hello JSON
- invalid relay hello
- relay credential resolved as an unexpected credential kind
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/abfd7dce43c49e48.
Report an issue: GitHub.