{"record":{"id":"df485aef6116d258","repo":"stablyai/orca","slug":"invalid-e2ee-v2-ready","errorCode":null,"errorMessage":"Invalid E2EE v2 ready","messagePattern":"Invalid E2EE v2 ready","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-e2ee-v2-physical-channel.ts","lineNumber":133,"sourceCode":"    } else if (typeof plaintext === 'string') {\n      this.args.onText(plaintext)\n    } else {\n      this.args.onBinary(plaintext)\n    }\n  }\n\n  private acceptReady(raw: unknown): void {\n    if (typeof raw !== 'string') {\n      throw new Error('Expected plaintext E2EE v2 ready')\n    }\n    let ready: unknown\n    try {\n      ready = JSON.parse(raw)\n    } catch {\n      throw new Error('Invalid E2EE v2 ready JSON')\n    }\n    if (!this.args.session.acceptReady(ready)) {\n      throw new Error('Invalid E2EE v2 ready')\n    }\n    this.state = 'awaiting-authenticated'\n    this.outboundQueue.enqueue({\n      kind: 'text',\n      plaintext: JSON.stringify({\n        type: 'e2ee_auth',\n        v: 2,\n        transcriptHashB64: this.args.session.transcriptHashB64,\n        deviceToken: this.args.deviceToken\n      })\n    })\n  }\n\n  private async openBinary(raw: unknown, generation: number): Promise<Uint8Array | null> {\n    const bytes = await this.args.decodeBinary(raw)\n    if (!bytes || generation !== this.generation) {\n      return null\n    }","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-e2ee-v2-physical-channel.ts#L115-L151","documentation":"Thrown by `acceptReady` when the ready JSON parsed successfully but `session.acceptReady(ready)` returned `false`. That boolean is false when `validateMobileE2EEV2Handshake` rejects the shape, or when the desktop public key in the handshake does not constant-time-equal the pinned key. This is the cryptographic pin check failing.","triggerScenarios":"Desktop's `desktopPublicKey` in the ready message differs from the `pinnedDesktopPublicKey` configured at session creation; ready message schema mismatch (wrong fields, wrong version, missing nonce); a different host answered the relay connection than the one the mobile pinned.","commonSituations":"Pinning the wrong public key (stale host profile after a desktop re-install); connecting to the wrong host via relay; a man-in-the-middle attempt that cannot produce the pinned key; protocol version skew where the ready schema changed.","solutions":["Confirm the `desktopPublicKeyB64` passed to `MobileE2EEV2ClientSession.create` matches the host's current long-term key (re-pair if the desktop rotated keys).","Inspect the ready payload against `validateMobileE2EEV2Handshake` to see whether schema or pin failed.","Abort the link on this error — do not retry with the same pin."],"exampleFix":"// before\nsession.acceptReady(ready) // silently false, then channel throws\n\n// after\nconst handshake = validateMobileE2EEV2Handshake(session.hello, ready)\nif (!handshake) reportError('ready schema rejected')\nelse if (!equalBytes(handshake.desktopPublicKey, pinnedDesktopPublicKey))\n  reportError('desktop key pin mismatch — re-pair required')","handlingStrategy":"validation","validationCode":"import { validateMobileE2EEV2Handshake } from '../../../src/shared/mobile-e2ee-v2-contract'\nconst handshake = validateMobileE2EEV2Handshake(session.hello, readyPayload)\nif (!handshake || !equalBytes(handshake.desktopPublicKey, pinnedDesktopPublicKey)) {\n  // abort before acceptReady\n}","typeGuard":"function pinsDesktopKey(handshake: { desktopPublicKey: Uint8Array } | null, pinned: Uint8Array): boolean {\n  return !!handshake && equalBytes(handshake.desktopPublicKey, pinned)\n}","tryCatchPattern":"channel.onError = (e) => { if (e.message === 'Invalid E2EE v2 ready') { markHostForRepair(hostId); link.close() } }","preventionTips":["Keep the pinned desktop public key in sync with the host's current long-term key.","Treat a pin mismatch as a re-pair trigger, never a retry.","Validate the ready schema before trusting any field."],"tags":["e2ee","handshake","public-key-pinning","security"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}