{"record":{"id":"267368510e7a34c0","repo":"tinyhumansai/openhuman","slug":"transport-local-http-response-status-text","errorCode":null,"errorMessage":"[transport:local] HTTP ${response.status}: ${text || response.statusText}","messagePattern":"\\[transport:local\\] HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/transport/LocalTransport.ts","lineNumber":77,"sourceCode":"    try {\n      response = await fetch(rpcUrl, {\n        method: 'POST',\n        headers,\n        body: JSON.stringify(payload),\n        signal: controller.signal,\n      });\n    } catch (err) {\n      if (controller.signal.aborted) {\n        throw new Error(`[transport:local] ${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: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,","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/transport/LocalTransport.ts#L59-L95","documentation":"Thrown by LocalTransport.request when the embedded core's HTTP RPC endpoint answers with a non-2xx status. The message embeds the HTTP status plus the response body (or statusText when the body is empty), so the real cause is whatever the core's /rpc endpoint reported. Most often it is 401 from a stale per-launch bearer token or 5xx from a core-side handler failure.","triggerScenarios":"Any transport.call() (i.e. every coreRpcClient request via relay_http_rpc) when fetch resolves with response.ok === false: POST to http://127.0.0.1:<port>/rpc returning 401 (stale hex bearer / mismatched OPENHUMAN_CORE_TOKEN), 404 (port from a previous core launch), 400 (malformed JSON-RPC body), or 500 (core handler error).","commonSituations":"Core restarted and got a new ephemeral port+token while the renderer cached the old pair; OPENHUMAN_CORE_REUSE_EXISTING=1 pointing at an external core whose token differs from {workspace}/core.token; core process crashed mid-request; a loopback-intercepting proxy or antivirus rewriting the response.","solutions":["Parse the embedded status: 401 means stale token/URL — re-fetch via the Tauri commands core_rpc_url and core_rpc_token, rebuild the transport, retry once","Verify the core process is alive (restart_core_process / daemonHealthService) and retry after it reports healthy","For external-core debugging, confirm OPENHUMAN_CORE_TOKEN matches the token written to {workspace}/core.token","For 5xx, read the core file log for the handler panic and fix the domain-side error"],"exampleFix":"// before\nconst result = await transport.call('openhuman.ping', {});\n\n// after\ntry {\n  const result = await transport.call('openhuman.ping', {});\n} catch (err) {\n  const m = /\\[transport:local\\] HTTP (\\d+): (.*)/.exec(String(err.message));\n  if (m && m[1] === '401') {\n    await rebuildTransportFromShell(); // re-fetch URL + bearer, retry once\n  } else {\n    throw err;\n  }\n}","handlingStrategy":"try-catch","validationCode":"async function coreRpcReady(rpcUrl: string, token: string): Promise<boolean> {\n  try {\n    const res = await fetch(rpcUrl, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n      body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'openhuman.ping', params: {} }),\n    });\n    return res.ok;\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"Catch, then parse the `HTTP <status>` prefix from err.message: on 401 rebuild the transport from freshly fetched core_rpc_url/core_rpc_token and retry once; on 5xx surface the embedded body text as the core error; otherwise rethrow.","preventionTips":["Always obtain URL and bearer through the core_rpc_url / core_rpc_token Tauri commands instead of caching them across core restarts","Treat a 401 as 'transport stale' — rebuild, never blindly retry the same transport","Wait on daemonHealthService readiness before the first RPC after boot"],"tags":["transport","local","http","json-rpc","core","auth"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}