tinyhumansai/openhuman · error · Error

RPC ${method} returned non-JSON HTTP ${res.status}: ${text.s

Error message

RPC ${method} returned non-JSON HTTP ${res.status}: ${text.slice(0, 200)}

What it means

After the HTTP response arrives, rpc() reads it as text and JSON.parse's it; if parsing fails it throws this error including the HTTP status and the first 200 bytes of the body. The JSON-RPC endpoint should always speak JSON, so a non-JSON body means the request never reached the real core handler — the URL hit some other server or an intermediary.

Source

Thrown at scripts/debug/goals-live.mjs:206

        jsonrpc: "2.0",
        id: `goals-${Date.now()}-${Math.random().toString(16).slice(2)}`,
        method,
        params,
      }),
    });
  } catch (err) {
    if (err?.name === "AbortError")
      throw new Error(`RPC ${method} timed out after ${timeoutMs}ms`);
    throw err;
  } finally {
    clearTimeout(timer);
  }
  const text = await res.text();
  let body;
  try {
    body = JSON.parse(text);
  } catch {
    throw new Error(`RPC ${method} returned non-JSON HTTP ${res.status}: ${text.slice(0, 200)}`);
  }
  if (!res.ok) throw new Error(`RPC ${method} HTTP ${res.status}`);
  if (body.error)
    throw new Error(`RPC ${method} error: ${body.error.message || JSON.stringify(body.error)}`);
  return body.result;
}

// RpcOutcome serializes either as the bare value (no logs) or { result, logs }.
function unwrap(result) {
  if (result && typeof result === "object" && "result" in result && "logs" in result) {
    return { value: result.result, logs: result.logs || [] };
  }
  return { value: result, logs: [] };
}

function renderGoals(doc) {
  const items = doc?.items || [];
  if (items.length === 0) return "    (no goals)";

View on GitHub (pinned to a221052e0d)

Solutions

  1. Inspect the body snippet in the message — HTML usually names the offending server (nginx, Vite, a portal)
  2. Point --core-url exactly at the core's JSON-RPC endpoint, e.g. http://127.0.0.1:7788/rpc (path /rpc included)
  3. Bypass proxies for loopback: `NO_PROXY=127.0.0.1 no_proxy=127.0.0.1 node ...`
  4. Sanity-check the endpoint with curl: `curl -s http://127.0.0.1:7788/health` and `GET /schema`
  5. If the core was spawned by the script, rerun with --verbose to see whether it crashed mid-request

Example fix

// before
OPENHUMAN_CORE_RPC_URL=http://127.0.0.1:5173/rpc node scripts/debug/goals-live.mjs
# Error: RPC core.ping returned non-JSON HTTP 200: <!DOCTYPE html>...

// after
node scripts/debug/goals-live.mjs --core-url http://127.0.0.1:7788/rpc
Defensive patterns

Strategy: validation

Validate before calling

// verify the endpoint speaks JSON-RPC before the run
const res = await fetch(coreUrl, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "core.ping", params: {} }),
});
const text = await res.text();
let ok = res.ok;
try { JSON.parse(text); } catch { ok = false; }
if (!ok) {
  console.error(`${coreUrl} is not a JSON-RPC endpoint (HTTP ${res.status}, body: ${text.slice(0, 120)})`);
  process.exit(2);
}

Type guard

const looksLikeJsonRpcEndpoint = (text) => {
  try { const b = JSON.parse(text); return "jsonrpc" in b || "result" in b || "error" in b; }
  catch { return false; }
};

Try / catch

try {
  body = JSON.parse(text);
} catch {
  throw new Error(`${coreUrl} returned non-JSON (HTTP ${res.status}) — wrong URL or proxy; first bytes: ${text.slice(0, 80)}`);
}

Prevention

When it happens

Trigger: --core-url (or OPENHUMAN_CORE_RPC_URL) pointing at a non-core server: the Vite dev server (5173), a proxy returning an HTML 502/503 page, a portal/captive page, or the core root path without /rpc; a corporate proxy intercepting 127.0.0.1 traffic; a truncated body because the process died mid-response.

Common situations: Port confusion between dev servers and the core's 7788 default; OPENHUMAN_CORE_RPC_URL inherited from a different session pointing at a since-restarted service; docker/network environments where the loopback proxy rewrites responses; the spawned core dying right after accepting the connection.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/c8d60f18fb3f1ec6. Report an issue: GitHub.