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

  1. Omit clientNonce in production code so ExpoCrypto.getRandomBytes(32) generates the correct length.
  2. In tests, pass clientNonce: ExpoCrypto.getRandomBytes(32) or a known 32-byte Uint8Array.
  3. Validate nonce length at the call site before passing to create().
  4. 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

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


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