{"record":{"id":"3f85a3d2205d9b7f","repo":"tinyhumansai/openhuman","slug":"rpc-method-http-res-status","errorCode":null,"errorMessage":"RPC ${method} HTTP ${res.status}","messagePattern":"RPC (.+?) HTTP (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/debug/agent-prepare-context-audit.mjs","lineNumber":281,"sourceCode":"      }),\n    });\n  } catch (err) {\n    if (err?.name === \"AbortError\")\n      throw new Error(`RPC ${method} timed out after ${timeoutMs}ms`);\n    throw err;\n  } finally {\n    clearTimeout(timeout);\n  }\n  const bodyText = await res.text();\n  let body;\n  try {\n    body = JSON.parse(bodyText);\n  } catch {\n    throw new Error(\n      `RPC ${method} returned non-JSON HTTP ${res.status}: ${bodyText.slice(0, 200)}`,\n    );\n  }\n  if (!res.ok) throw new Error(`RPC ${method} HTTP ${res.status}`);\n  if (body.error)\n    throw new Error(\n      `RPC ${method} error: ${JSON.stringify(body.error).slice(0, 300)}`,\n    );\n  return body.result;\n}\n\n// ── Transcript reading ──────────────────────────────────────────────────────\n\nasync function walkJsonl(dir) {\n  const out = [];\n  async function walk(current) {\n    let entries;\n    try {\n      entries = await readdir(current, { withFileTypes: true });\n    } catch {\n      return;\n    }","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/debug/agent-prepare-context-audit.mjs#L263-L299","documentation":"rpc() in scripts/debug/agent-prepare-context-audit.mjs parses the response body as JSON successfully, then checks res.ok; any non-2xx status throws 'RPC <method> HTTP <status>'. This is transport-level rejection at the HTTP layer — the server spoke valid JSON (often a JSON error object) but the status says the request itself was not accepted.","triggerScenarios":"401 when the bearer token is wrong/revoked (e.g. --token from a dead core's core.token while attaching to a newly spawned core with a fresh token); 403 for permission failures; 404 when the URL path is wrong (missing /rpc or a proxy route); 500 when the core's HTTP layer fails before JSON-RPC dispatch. Distinct from the non-JSON error (212), which fires when the body isn't even parseable.","commonSituations":"Mixing tokens between core runs — core.token is regenerated per launch, so a stale file yields 401; pointing at a core built with different feature gates so the route shape differs; reverse proxies returning 502 while the core restarts underneath the audit.","solutions":["On 401: re-fetch the token — cat the current workspace's core.token, or pass --spawn-core so the script generates and uses a matching token","On 404: verify the URL includes /rpc and the core is the openhuman-core JSON-RPC server","On 5xx: check the core's own logs (file-only core log stream) for the underlying failure and restart it","Retry after the core is healthy — this is a state error, not a bad request shape"],"exampleFix":"# before\n$ OPENHUMAN_CORE_TOKEN=stale-token node scripts/debug/agent-prepare-context-audit.mjs\nError: RPC core.ping HTTP 401\n\n# after\n$ node scripts/debug/agent-prepare-context-audit.mjs --token \"$(cat ~/.openhuman/users/<id>/workspace/core.token)\"","handlingStrategy":"try-catch","validationCode":"// Cheap auth probe before the expensive audit calls\nconst res = await fetch(coreUrl, { method: \"POST\", headers: { \"content-type\": \"application/json\", authorization: `Bearer ${token}` }, body: JSON.stringify({ jsonrpc: \"2.0\", id: 1, method: \"core.ping\", params: {} }) });\nif (res.status === 401) throw new Error(\"bearer rejected (401) — re-read core.token from the running core's workspace or pass --token\");\nif (res.status === 404) throw new Error(\"route not found (404) — URL must include /rpc\");","typeGuard":null,"tryCatchPattern":"try {\n  return await rpc(coreUrl, token, method, params);\n} catch (e) {\n  const m = /RPC \\S+ HTTP (\\d+)/.exec(e.message);\n  if (m) {\n    const status = Number(m[1]);\n    if (status === 401 || status === 403) throw new Error(`auth failed (${status}) — refresh the token from the live core`, { cause: e });\n    if (status >= 500) { await sleep(5_000); return rpc(coreUrl, token, method, params); } // core restarting\n  }\n  throw e;\n}","preventionTips":["Tokens are per-launch: never reuse a core.token from a previous core run when attaching to a new one","Probe with core.ping before kicking off a long audit — 401 there costs milliseconds instead of minutes","5xx right after --spawn-core usually means the core is still binding; wait and retry once"],"tags":["rpc","http","auth","status-code"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}