{"record":{"id":"87ca003f6e3abaff","repo":"stablyai/orca","slug":"invalid-relay-hello","errorCode":null,"errorMessage":"invalid relay hello","messagePattern":"invalid relay hello","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-relay-e2ee-link.ts","lineNumber":118,"sourceCode":"        .catch((error: unknown) => this.fail(asError(error)))\n    }\n    this.socket.onerror = () => this.fail(new Error('relay transport error'))\n    this.socket.onclose = (event) => this.fail(new RelayOuterError(event.code || 1006))\n  }\n\n  private acceptHello(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 !== this.options.expectedCredentialKind) {\n      throw new Error('relay credential resolved as an unexpected credential kind')\n    }\n    this.outerReady = true\n    this.options.onHello?.(parsed.data)\n    this.channel.start()\n  }\n\n  private fail(error: Error): void {\n    if (this.closed) {\n      return\n    }\n    this.closed = true\n    this.channel.dispose()","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-relay-e2ee-link.ts#L100-L136","documentation":"Thrown when RelayPhoneHelloSchema.safeParse(value) fails: the first frame parsed as JSON but does not match the relay-hello union (type literal 'relay-hello', ok flag, credentialKind 'invite'|'resume', leaseExpiresAt, and for resume the acceptedCredentialVersion/acceptedAs/resumeExpiresAt fields).","triggerScenarios":"The cell sent valid JSON that is not a relay-hello: a 'relay-moved' notice (host reassigned to a new cellUrl), an error object, or a hello missing/adding required fields under the .strict() schema.","commonSituations":"Client/cell version skew where a newer cell changed the hello shape; the host was reassigned and the cell advertises relay-moved instead of relay-hello; the endpoint URL reached a different relay service that speaks another protocol.","solutions":["Capture parsed.error.issues from a local safeParse to see which field failed.","If the frame is relay-moved, re-resolve the endpoint via the resume director before reconnecting.","Align client and relay-cell versions so the hello schema matches.","Verify cellUrl and relayHostId target the intended cell."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-validate a captured frame against the same schema the cell should use\nimport { RelayPhoneHelloSchema } from '../../../src/shared/mobile-relay-phone-protocol'\nconst ok = RelayPhoneHelloSchema.safeParse(JSON.parse(capturedFrame)).success","typeGuard":"import { RelayPhoneHelloSchema, type RelayPhoneHello } from '../../../src/shared/mobile-relay-phone-protocol'\nfunction isRelayHello(value: unknown): value is RelayPhoneHello {\n  return RelayPhoneHelloSchema.safeParse(value).success\n}","tryCatchPattern":"try {\n  const link = new MobileRelayE2eeLink(options)\n} catch (error) {\n  if (error instanceof Error && error.message === 'invalid relay hello') {\n    // re-resolve endpoint via director in case the cell sent relay-moved\n  }\n}","preventionTips":["Keep the client's mobile-relay-phone-protocol schema version in lockstep with the relay cell build.","Handle relay-moved distinctly from a malformed hello by re-resolving the endpoint.","Log parsed.error issues when reproducing against a staging cell."],"tags":["relay","websocket","schema","zod","e2ee"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}