stablyai/orca · error · Error

E2EE v2 ready has not been accepted

Error message

E2EE v2 ready has not been accepted

What it means

Thrown by the `transcriptHashB64` getter on `MobileE2EEV2ClientSession` when the key schedule has not been derived yet. The getter is only meaningful after `acceptReady()` has successfully installed `this.schedule` and cached `transcriptHashB64Value`. Reaching the throw means the property was read before the v2 ready handshake completed (or before `acceptReady` returned true).

Source

Thrown at mobile/src/transport/mobile-e2ee-v2-client-session.ts:79

  acceptReady(ready: unknown): boolean {
    const handshake = validateMobileE2EEV2Handshake(this.hello, ready)
    if (!handshake || !equalBytes(handshake.desktopPublicKey, this.pinnedDesktopPublicKey)) {
      return false
    }
    this.schedule = deriveMobileE2EEV2KeySchedule({
      sharedSecret: deriveSharedKey(this.clientSecretKey, this.pinnedDesktopPublicKey),
      transcript: encodeMobileE2EEV2Transcript(handshake),
      clientNonce: handshake.clientNonce,
      desktopNonce: handshake.desktopNonce
    })
    this.transcriptHashB64Value = encodeBase64(this.schedule.transcriptHash)
    return true
  }

  get transcriptHashB64(): string {
    if (!this.transcriptHashB64Value) {
      throw new Error('E2EE v2 ready has not been accepted')
    }
    return this.transcriptHashB64Value
  }

  openText(frameB64: string): string | null {
    const frame = decodeCanonicalBase64(frameB64)
    if (!frame) {
      return null
    }
    const plaintext = this.open(frame, 'text')
    return plaintext ? new TextDecoder().decode(plaintext) : null
  }

  openBinary(frame: Uint8Array): Uint8Array | null {
    return this.open(frame, 'binary')
  }

  sealText(plaintext: string): string {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Ensure `acceptReady(ready)` was called with a valid ready payload and returned `true` before reading `transcriptHashB64`.
  2. Gate the read on the physical channel's state machine — only the `awaiting-authenticated` branch in `MobileE2EEV2PhysicalChannel.acceptReady` is allowed to read it.
  3. In tests, drive the session through `acceptReady` with a handshake whose `desktopPublicKey` equals the pinned key, or expose a test helper that derives the schedule deterministically.

Example fix

// before
const session = MobileE2EEV2ClientSession.create({ desktopPublicKeyB64, transport: 'relay' })
const hash = session.transcriptHashB64 // throws

// after
if (!session.acceptReady(validReadyPayload)) {
  throw new Error('handshake rejected')
}
const hash = session.transcriptHashB64
Defensive patterns

Strategy: validation

Validate before calling

// Track acceptance externally; only read the getter after acceptReady returns true.
const accepted = session.acceptReady(readyPayload)
if (!accepted) return // do not read transcriptHashB64
const hash = session.transcriptHashB64

Type guard

// No public isAccepted() exists; derive it from the side effect:
function sessionAccepted(session: MobileE2EEV2ClientSession): boolean {
  try { void session.transcriptHashB64; return true } catch { return false }
}

Try / catch

try { const hash = session.transcriptHashB64 } catch (e) { if (e.message === 'E2EE v2 ready has not been accepted') { /* defer this read until onAuthenticated */ } else throw e }

Prevention

When it happens

Trigger: Calling `session.transcriptHashB64` before `session.acceptReady(ready)` returned `true`. This includes reading it inside a constructor, in a test that skipped the handshake step, or from any code path that races the getter against the awaiting-ready state.

Common situations: Unit tests that construct a session via `MobileE2EEV2ClientSession.create(...)` and assert on `transcriptHashB64` without first feeding a valid desktop ready message; refactors that moved the transcript-hash read upstream of `acceptReady`; a caller that ignores the boolean return of `acceptReady` and proceeds as if accepted.

Related errors


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