{"record":{"id":"16485feffbff1998","repo":"tinyhumansai/openhuman","slug":"lan-rpc-returned-an-error","errorCode":null,"errorMessage":"LAN RPC returned an error","messagePattern":"LAN RPC returned an error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/transport/LanHttpTransport.ts","lineNumber":76,"sourceCode":"    } catch (err) {\n      if (controller.signal.aborted) {\n        throw new Error(`[transport:lan] ${method} timed out after ${this.timeoutMs}ms`);\n      }\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","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/services/transport/LanHttpTransport.ts#L58-L94","documentation":"The LAN core answered 2xx with a JSON-RPC error envelope whose message was null/undefined, so the generic 'LAN RPC returned an error' text is used. The debug line ('[transport:lan] ← %s error') logged json.error.message just before, and json.error.code is available on the wire frame even though this plain Error drops it.","triggerScenarios":"Calling a method the desktop core doesn't register while on a LAN profile (gated domain off in that core build, or app/core version skew); server-side validation failures whose handler omits the message field.","commonSituations":"iOS client newer than the desktop build (method added later); desktop running a slim feature set (--no-default-features) that omitted a family the mobile UI expects; drifted method names between versions.","solutions":["Enable DEBUG='transport:lan:error' to capture the logged json.error.message/code pair","Check the desktop core's /schema for the method before the mobile UI calls it","Update the desktop app so both ends share the method surface","Gate UI features on a capability/schema probe rather than assuming method availability"],"exampleFix":"// before\nconst r = await transport.call('openhuman.meet_list_upcoming', {});\n\n// after\nconst schema = await transport.call<{ methods: string[] }>('core.schema', {});\nif (!schema.methods.includes('openhuman.meet_list_upcoming')) throw new Error('meet not available on this core');\nconst r = await transport.call('openhuman.meet_list_upcoming', {});","handlingStrategy":"try-catch","validationCode":"async function lanCoreHasMethod(transport: LanHttpTransport, method: string): Promise<boolean> {\n  const schema = await transport.call<{ methods: string[] }>('core.schema', {});\n  return schema.methods.includes(method);\n}","typeGuard":"function isLanEnvelopeError(err: unknown): boolean {\n  return err instanceof Error && (err.message === 'LAN RPC returned an error' || err.message.startsWith('[transport:lan]'));\n}","tryCatchPattern":"try {\n  return await transport.call<T>(method, params);\n} catch (err) {\n  if (isLanEnvelopeError(err)) {\n    // DEBUG='transport:lan:error' logged json.error.code/message — correlate before reporting\n    logger.error('LAN core rejected call', { method });\n  }\n  throw err;\n}","preventionTips":["Schema-probe the desktop core before calling domain methods from mobile UI","Keep the desktop app updated so both ends share the method surface","Capture the logged json.error.code — the thrown message intentionally omits it"],"tags":["transport","lan","json-rpc","unknown-method","version-skew"],"backgroundTag":"json-rpc-error-response","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}