stablyai/orca · error · Error

Invalid E2EE v2 authenticated response

Error message

Invalid E2EE v2 authenticated response

What it means

Thrown in the `awaiting-authenticated` state when the decrypted plaintext is a string but does not satisfy `isAuthenticated` — i.e. it is not a JSON object with exactly the keys `transcriptHashB64,type,v`, or `type !== 'e2ee_authenticated'`, or `v !== 2`, or the transcript hash does not equal the session's. Unlike an explicit `e2ee_error` rejection, this is a malformed or unexpected authenticated-shaped response.

Source

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

    }
    if (this.state === 'awaiting-ready') {
      this.acceptReady(raw)
      return
    }

    const plaintext =
      typeof raw === 'string'
        ? this.args.session.openText(raw)
        : await this.openBinary(raw, generation)
    if (generation !== this.generation || plaintext === null) {
      return
    }
    if (this.state === 'awaiting-authenticated') {
      if (typeof plaintext === 'string' && isAuthenticationRejection(plaintext)) {
        throw new MobileE2EEAuthenticationError()
      }
      if (typeof plaintext !== 'string' || !this.isAuthenticated(plaintext)) {
        throw new Error('Invalid E2EE v2 authenticated response')
      }
      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 {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Confirm desktop and mobile are on the same v2 e2ee contract version.
  2. Inspect the decrypted plaintext (via a debug hook) to see which `isAuthenticated` branch failed — extra keys, wrong type, or hash mismatch each point to a different cause.
  3. Treat as fatal for the link and tear down + retry the full handshake.

Example fix

// before
onError: (e) => log(e.message) // no recovery

// after
onError: (e) => {
  if (e.message === 'Invalid E2EE v2 authenticated response') {
    reportProtocolMismatchAndDisconnect(link)
  } else { /* ... */ }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// No preventive call: the desktop sent a bad authenticated message. Validate after decrypt in a debug hook.

Type guard

function isAuthenticatedShape(msg: unknown): boolean {
  if (typeof msg !== 'object' || msg === null) return false
  const keys = Object.keys(msg).sort().join(',')
  return keys === 'transcriptHashB64,type,v'
}

Try / catch

channel.onError = (e) => { if (e.message === 'Invalid E2EE v2 authenticated response') { reportProtocolMismatch(link); link.close() } else { /* ... */ } }

Prevention

When it happens

Trigger: Desktop sent a non-rejection, non-authenticated plaintext during the auth window (e.g. a stray text frame); transcript-hash mismatch where neither side sent `e2ee_error`; a version-skewed desktop that uses a different authenticated schema or extra keys.

Common situations: Protocol version drift between mobile and desktop; a desktop bug that ships a mis-specified `e2ee_authenticated` message with extra fields; man-in-the-middle or corrupted frame that decrypts to plausible-but-wrong JSON.

Understand the failure class

Related errors


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