{"record":{"id":"3974d8423daf69fd","repo":"stablyai/orca","slug":"invalid-e2ee-v2-ready-json","errorCode":null,"errorMessage":"Invalid E2EE v2 ready JSON","messagePattern":"Invalid E2EE v2 ready JSON","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-e2ee-v2-physical-channel.ts","lineNumber":130,"sourceCode":"      }\n      this.state = 'ready'\n      this.args.onAuthenticated()\n    } 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)","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-e2ee-v2-physical-channel.ts#L112-L148","documentation":"Thrown by `acceptReady` when the first frame is a string but `JSON.parse` throws — the ready payload is not valid JSON. The catch block re-wraps the parse failure so callers see a single, specific cause rather than a raw `SyntaxError`.","triggerScenarios":"Desktop sent a ready frame that is plain text but not JSON (e.g. an HTTP error body, a debug log, or a partial/truncated frame); a proxy or relay injected non-JSON text; encoding mismatch (UTF-16 BOM, etc.) that breaks the JSON lexer.","commonSituations":"A misbehaving relay returning an error page on the WebSocket; desktop build that logs to the same channel; truncation at a transport boundary producing half a JSON document.","solutions":["Log the raw first frame to identify what non-JSON content arrived.","Verify the relay/desktop endpoint is the intended one and not returning an HTTP/HTML error body.","Ensure the channel is not shared with debug logging."],"exampleFix":"// before\n// opaque: only see \"Invalid E2EE v2 ready JSON\"\n\n// after\nprivate acceptReady(raw: unknown): void {\n  if (typeof raw !== 'string') throw new Error('Expected plaintext E2EE v2 ready')\n  let ready: unknown\n  try { ready = JSON.parse(raw) }\n  catch (e) { throw new Error(`Invalid E2EE v2 ready JSON: ${raw.slice(0, 120)}`) }\n  // ...\n}","handlingStrategy":"try-catch","validationCode":"function isJsonString(s: string): boolean { try { JSON.parse(s); return true } catch { return false } }\nif (typeof firstFrame === 'string' && !isJsonString(firstFrame)) { /* log raw, close */ }","typeGuard":null,"tryCatchPattern":"channel.onError = (e) => { if (e.message === 'Invalid E2EE v2 ready JSON') { logRawFirstFrameForDiagnosis(); link.close() } }","preventionTips":["Log the raw first frame to identify non-JSON content (error pages, debug logs).","Verify the relay/desktop endpoint returns the ready handshake, not an error body.","Guard the channel against shared debug logging."],"tags":["e2ee","handshake","json","parsing"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}