{"record":{"id":"acc432f4511df35a","repo":"stablyai/orca","slug":"invalid-relay-hello-json-acc432","errorCode":null,"errorMessage":"invalid relay hello JSON","messagePattern":"invalid relay hello JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-relay-physical-client.ts","lineNumber":125,"sourceCode":"          acceptRelayHello(event.data)\n          return\n        }\n        await channel.handleMessage(event.data)\n      })\n      .catch((error: unknown) => fail(asError(error)))\n  }\n  socket.onerror = () => fail(new Error('relay transport error'))\n  socket.onclose = (event) => fail(new RelayOuterError(event.code || 1006))\n\n  function acceptRelayHello(raw: unknown): void {\n    if (typeof raw !== 'string') {\n      throw new Error('expected plaintext relay hello')\n    }\n    let value: unknown\n    try {\n      value = JSON.parse(raw)\n    } catch {\n      throw new Error('invalid relay hello JSON')\n    }\n    const parsed = RelayPhoneHelloSchema.safeParse(value)\n    if (!parsed.success) {\n      throw new Error('invalid relay hello')\n    }\n    if (!parsed.data.ok) {\n      throw new RelayOuterError(parsed.data.code)\n    }\n    if (parsed.data.credentialKind !== (args.expectedCredentialKind ?? 'invite')) {\n      throw new Error('relay credential resolved as an unexpected credential kind')\n    }\n    outerReady = true\n    log('info', 'Relay: cell accepted credential', 'Starting E2EE handshake')\n    channel.start()\n  }\n\n  function fail(error: Error): void {\n    if (closed) {","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-relay-physical-client.ts#L107-L143","documentation":"Thrown in connectMobileRelayForPairing's acceptRelayHello when the first WS frame is a string but JSON.parse throws. The cell's plaintext hello is not valid JSON during the pairing dial.","triggerScenarios":"The cell returned an HTML error page, a plain-text error string, or a partial frame as the first message while the client dials for pairing.","commonSituations":"cellUrl pointing at a load-balancer/error page; a captive portal interstitial; the cell redeployed mid-dial and emitted a non-JSON banner.","solutions":["Log the redacted raw first frame to identify the source.","Confirm cellUrl resolves to a relay cell, not an error/landing page.","Redial after confirming network connectivity.","Verify cell health and version."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const client = connectMobileRelayForPairing(args)\n} catch (error) {\n  if (error instanceof Error && error.message === 'invalid relay hello JSON') {\n    // log redacted raw frame; verify cellUrl; redial on a clean network\n  }\n}","preventionTips":["Validate relay.cellUrl is an https origin pointing at a relay cell before dialing.","Surface connection failures with a reconnect action rather than silent retry.","In tests, emit valid JSON relay-hello strings from createSocket mocks."],"tags":["relay","websocket","json","pairing"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}