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
- Ensure `acceptReady(ready)` was called with a valid ready payload and returned `true` before reading `transcriptHashB64`.
- Gate the read on the physical channel's state machine — only the `awaiting-authenticated` branch in `MobileE2EEV2PhysicalChannel.acceptReady` is allowed to read it.
- 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
- Read transcriptHashB64 only inside the awaiting-authenticated branch of the physical channel.
- Treat the boolean return of acceptReady as authoritative — never proceed on false.
- In tests, drive the full handshake before asserting on the hash.
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
- Invalid public key: expected 32 bytes, got ${key.length} fro
- Invalid client nonce length: ${clientNonce.length}
- Invalid ${label}: expected ${expected} bytes, got ${bytes.le
- Invalid E2EE v2 authenticated response
- Expected plaintext E2EE v2 ready
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/2ffc35ad84308032.
Report an issue: GitHub.