{"record":{"id":"298edcce806af82f","repo":"tinyhumansai/openhuman","slug":"transport-lan-response-missing-result","errorCode":null,"errorMessage":"[transport:lan] response missing result","messagePattern":"\\[transport:lan\\] response missing result","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/transport/LanHttpTransport.ts","lineNumber":79,"sourceCode":"      }\n      throw err;\n    } finally {\n      clearTimeout(timeoutId);\n    }\n\n    if (!response.ok) {\n      const text = await response.text();\n      throw new Error(`[transport:lan] HTTP ${response.status}: ${text || response.statusText}`);\n    }\n\n    const json = (await response.json()) as JsonRpcResponse<T>;\n\n    if (json.error) {\n      logErr('[transport:lan] ← %s error: %s', method, json.error.message);\n      throw new Error(json.error.message ?? 'LAN RPC returned an error');\n    }\n    if (!Object.prototype.hasOwnProperty.call(json, 'result')) {\n      throw new Error('[transport:lan] response missing result');\n    }\n\n    log('[transport:lan] ← %s id=%d ok', method, id);\n    return json.result as T;\n  }\n\n  async *stream<T>(\n    method: string,\n    params: unknown,\n    opts?: { signal?: AbortSignal }\n  ): AsyncIterable<T> {\n    const result = await this.call<T>(method, params, opts);\n    yield result;\n  }\n\n  async isHealthy(): Promise<boolean> {\n    try {\n      await this.call('openhuman.ping', {}, { signal: AbortSignal.timeout(2000) });","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/transport/LanHttpTransport.ts#L61-L97","documentation":"LAN transport protocol check: 2xx response, valid JSON, no `error` field, and no own `result` property — a malformed success frame. Identical semantics to the cloud/local variants: JSON-RPC 2.0 requires exactly one of `result`/`error`, and the transport fails rather than returning `undefined` as `T`.","triggerScenarios":"Desktop core (or something impersonating it on that port) answering `{}` or `{jsonrpc, id}` with no payload; a proxy on the LAN path rewriting the body; a very old core whose success shape predates the strict check.","commonSituations":"Captive portal or 'smart' router middleware intercepting HTTP on LAN; wrong port in the connection profile landing on another local service; version mismatch after updating only one side.","solutions":["curl the rpcUrl from the client device with a `openhuman.ping` frame and inspect the raw response","Confirm the profile's rpcUrl is the desktop core's `/rpc` route","Remove or bypass LAN middleboxes (router portals, proxies) for that host:port","Update both ends to matching versions"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { return await lan.call<T>(m, p); }\ncatch (e) {\n  if (e instanceof Error && e.message === '[transport:lan] response missing result') {\n    throw new Error(`LAN rpc url is not a JSON-RPC endpoint (method ${m}) — re-pair the connection profile`);\n  }\n  throw e;\n}","preventionTips":["Health-check with openhuman.ping when creating a LAN profile to catch wrong-endpoint configs at setup","Keep router portals/proxies off the core host:port — they rewrite frames","Update both ends together; old cores may emit frames without result"],"tags":["json-rpc","lan","validation","transport"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}