{"record":{"id":"cc39a7bd97153e65","repo":"tinyhumansai/openhuman","slug":"transport-local-response-missing-result","errorCode":null,"errorMessage":"[transport:local] response missing result","messagePattern":"\\[transport:local\\] response missing result","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/services/transport/LocalTransport.ts","lineNumber":87,"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:local] 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:local] ← %s error: %s', method, json.error.message);\n      throw new Error(json.error.message ?? 'Core RPC returned an error');\n    }\n    if (!Object.prototype.hasOwnProperty.call(json, 'result')) {\n      throw new Error('[transport:local] response missing result');\n    }\n\n    log('[transport:local] ← %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    // Local HTTP doesn't support streaming natively in this project.\n    // Fall back to a single call and yield the result.\n    const result = await this.call<T>(method, params, opts);\n    yield result;\n  }\n\n  async isHealthy(): Promise<boolean> {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/transport/LocalTransport.ts#L69-L105","documentation":"HTTP 200 from the local endpoint, but the parsed JSON has neither an `error` nor an own `result` property (checked via hasOwnProperty) — the body is not a JSON-RPC envelope at all. The transport refuses to guess and aborts.","triggerScenarios":"A 200 whose JSON is an empty object, a bare value, or middleware's own JSON (e.g. an auth layer returning {ok:false}); localRpcUrl pointing at the wrong endpoint such as /health instead of /rpc; a gateway rewriting the body.","commonSituations":"localRpcUrl misconfigured to a non-/rpc path; an external core behind a reverse proxy that rewrites or wraps response bodies; a mock server configured for a different method; a notification-style request (no id) getting an id-less reply.","solutions":["Log the raw response text before parsing to see what actually came back","Verify localRpcUrl is the exact /rpc path of the core's HTTP host","Ensure the request body is a well-formed JSON-RPC request with an id (notifications legitimately get no result)","Exclude /rpc from any proxy/middleware that rewrites response bodies"],"exampleFix":"// before\nconst json = (await response.json()) as JsonRpcResponse<T>;\n\n// after — surface what the 200 actually contained\nconst text = await response.text();\nlet json: JsonRpcResponse<T>;\ntry {\n  json = JSON.parse(text);\n} catch {\n  throw new Error(`[transport:local] non-JSON 200 body: ${text.slice(0, 200)}`);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isJsonRpcEnvelope(v: unknown): v is { result?: unknown; error?: { code?: number; message?: string } } {\n  return typeof v === 'object' && v !== null && ('result' in v || 'error' in v);\n}","tryCatchPattern":"Catch, then immediately log which URL was called — this error almost always means wrong endpoint or a rewriting proxy, not a flaky core; fix the endpoint instead of retrying.","preventionTips":["Point the transport only at the core's exact /rpc path","When adding core HTTP middleware, guarantee it never returns 200 with a non-envelope JSON body","Include the request id when logging so a notification-style reply is distinguishable"],"tags":["rpc","json-rpc","transport","protocol","config"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}