stablyai/orca · critical · Error
Invalid client nonce length: ${clientNonce.length}
Error message
Invalid client nonce length: ${clientNonce.length} What it means
MobileE2EEV2ClientSession.create received a clientNonce whose byte length is not exactly 32. The nonce is either caller-supplied (args.clientNonce) or generated by ExpoCrypto.getRandomBytes(32). A caller-supplied nonce of the wrong length trips this guard before the hello is constructed, since the E2EE v2 key schedule derives session keys from a 32-byte client nonce.
Source
Thrown at mobile/src/transport/mobile-e2ee-v2-client-session.ts:40
private constructor(
private readonly clientSecretKey: Uint8Array,
private readonly pinnedDesktopPublicKey: Uint8Array,
hello: MobileE2EEV2Hello
) {
this.hello = hello
}
static create(args: {
desktopPublicKeyB64: string
transport: MobileE2EETransport
relayHostId?: string
clientNonce?: Uint8Array
clientKeyPair?: { publicKey: Uint8Array; secretKey: Uint8Array }
}): MobileE2EEV2ClientSession {
const keyPair = args.clientKeyPair ?? generateKeyPair()
const clientNonce = args.clientNonce ?? ExpoCrypto.getRandomBytes(32)
if (clientNonce.length !== 32) {
throw new Error(`Invalid client nonce length: ${clientNonce.length}`)
}
return new MobileE2EEV2ClientSession(
keyPair.secretKey,
publicKeyFromBase64(args.desktopPublicKeyB64),
{
type: 'e2ee_hello',
v: 2,
clientPublicKeyB64: publicKeyToBase64(keyPair.publicKey),
clientNonceB64: encodeBase64(clientNonce),
capabilities: { framing: [2], payloadKinds: ['text', 'binary'] },
context: {
protocol: 'orca-mobile-e2ee',
initiator: 'mobile',
responder: 'desktop',
transport: args.transport,
...(args.relayHostId ? { relayHostId: args.relayHostId } : {})
}
}View on GitHub (pinned to 1136503c6a)
Solutions
- Omit clientNonce in production code so ExpoCrypto.getRandomBytes(32) generates the correct length.
- In tests, pass clientNonce: ExpoCrypto.getRandomBytes(32) or a known 32-byte Uint8Array.
- Validate nonce length at the call site before passing to create().
- Ensure the expo-crypto native module is properly linked in custom dev builds.
Example fix
// before (test passing wrong-length nonce)
const session = MobileE2EEV2ClientSession.create({
desktopPublicKeyB64,
transport: 'direct',
clientNonce: new Uint8Array(16) // wrong!
})
// after
const session = MobileE2EEV2ClientSession.create({
desktopPublicKeyB64,
transport: 'direct',
clientNonce: new Uint8Array(32) // or omit entirely
}) Defensive patterns
Strategy: validation
Validate before calling
function isValidClientNonce(nonce: Uint8Array): boolean {
return nonce instanceof Uint8Array && nonce.length === 32
}
// Validate before creating the session
if (clientNonce && !isValidClientNonce(clientNonce)) {
throw new Error(`clientNonce must be 32 bytes, got ${clientNonce.length}`)
} Type guard
function isNonce32(nonce: unknown): nonce is Uint8Array {
return nonce instanceof Uint8Array && nonce.length === 32
} Try / catch
try {
const session = MobileE2EEV2ClientSession.create({ desktopPublicKeyB64, transport, clientNonce })
} catch (e) {
if (e.message.startsWith('Invalid client nonce length')) {
// Regenerate the nonce with the correct length
session = MobileE2EEV2ClientSession.create({ desktopPublicKeyB64, transport })
}
} Prevention
- Omit clientNonce in production to let ExpoCrypto.getRandomBytes(32) generate it correctly.
- In tests, always pass a 32-byte Uint8Array or the real ExpoCrypto.getRandomBytes(32).
- Validate nonce length at the call site before passing to create().
- Ensure expo-crypto is properly linked in custom dev builds.
When it happens
Trigger: A test or caller passed a clientNonce of the wrong length (e.g. 16 bytes from nacl.randomBytes(16)); expo-crypto.getRandomValues was shimmed incorrectly in a test environment returning wrong-length output; a nonce was truncated or double-encoded before being passed.
Common situations: Unit test mocking ExpoCrypto.getRandomBytes to return a short buffer; a caller reusing a nonce from a different protocol with a different length; nonce deserialized from base64 incorrectly (off-by-one decode); dev-build expo-crypto native module misconfigured.
Related errors
- Invalid public key: expected 32 bytes, got ${key.length} fro
- Invalid ${label}: expected ${expected} bytes, got ${bytes.le
- Invalid accounts snapshot from host
- Invalid reset response from host
- E2EE v2 ready has not been accepted
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/c524d1f3aeaad405.
Report an issue: GitHub.