stablyai/orca · error · Error

Invalid E2EE v2 ready JSON

Error message

Invalid E2EE v2 ready JSON

What it means

Thrown by `acceptReady` when the first frame is a string but `JSON.parse` throws — the ready payload is not valid JSON. The catch block re-wraps the parse failure so callers see a single, specific cause rather than a raw `SyntaxError`.

Source

Thrown at mobile/src/transport/mobile-e2ee-v2-physical-channel.ts:130

      }
      this.state = 'ready'
      this.args.onAuthenticated()
    } else if (typeof plaintext === 'string') {
      this.args.onText(plaintext)
    } else {
      this.args.onBinary(plaintext)
    }
  }

  private acceptReady(raw: unknown): void {
    if (typeof raw !== 'string') {
      throw new Error('Expected plaintext E2EE v2 ready')
    }
    let ready: unknown
    try {
      ready = JSON.parse(raw)
    } catch {
      throw new Error('Invalid E2EE v2 ready JSON')
    }
    if (!this.args.session.acceptReady(ready)) {
      throw new Error('Invalid E2EE v2 ready')
    }
    this.state = 'awaiting-authenticated'
    this.outboundQueue.enqueue({
      kind: 'text',
      plaintext: JSON.stringify({
        type: 'e2ee_auth',
        v: 2,
        transcriptHashB64: this.args.session.transcriptHashB64,
        deviceToken: this.args.deviceToken
      })
    })
  }

  private async openBinary(raw: unknown, generation: number): Promise<Uint8Array | null> {
    const bytes = await this.args.decodeBinary(raw)

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Log the raw first frame to identify what non-JSON content arrived.
  2. Verify the relay/desktop endpoint is the intended one and not returning an HTTP/HTML error body.
  3. Ensure the channel is not shared with debug logging.

Example fix

// before
// opaque: only see "Invalid E2EE v2 ready JSON"

// after
private acceptReady(raw: unknown): void {
  if (typeof raw !== 'string') throw new Error('Expected plaintext E2EE v2 ready')
  let ready: unknown
  try { ready = JSON.parse(raw) }
  catch (e) { throw new Error(`Invalid E2EE v2 ready JSON: ${raw.slice(0, 120)}`) }
  // ...
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isJsonString(s: string): boolean { try { JSON.parse(s); return true } catch { return false } }
if (typeof firstFrame === 'string' && !isJsonString(firstFrame)) { /* log raw, close */ }

Try / catch

channel.onError = (e) => { if (e.message === 'Invalid E2EE v2 ready JSON') { logRawFirstFrameForDiagnosis(); link.close() } }

Prevention

When it happens

Trigger: Desktop sent a ready frame that is plain text but not JSON (e.g. an HTTP error body, a debug log, or a partial/truncated frame); a proxy or relay injected non-JSON text; encoding mismatch (UTF-16 BOM, etc.) that breaks the JSON lexer.

Common situations: A misbehaving relay returning an error page on the WebSocket; desktop build that logs to the same channel; truncation at a transport boundary producing half a JSON document.

Related errors


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