tinyhumansai/openhuman · error · Error

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

Error message

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

What it means

After the fetch completes, harness-cache-audit's rpc() JSON.parses the response text and throws this error when parsing fails. The core's /rpc endpoint always emits JSON, so a non-JSON body indicates the request terminated at some other HTTP speaker — wrong port/path, a proxy error page, or a server crash mid-body. Note this script's message omits the body snippet that goals-live.mjs includes, so you get only the status code.

Source

Thrown at scripts/debug/harness-cache-audit.mjs:227

        id: `audit-${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(timeout);
  }
  const bodyText = await res.text();
  let body;
  try {
    body = JSON.parse(bodyText);
  } catch {
    throw new Error(`RPC ${method} returned non-JSON HTTP ${res.status}`);
  }
  if (!res.ok) {
    throw new Error(`RPC ${method} HTTP ${res.status}`);
  }
  if (body.error) {
    throw new Error(`RPC ${method} error`);
  }
  return body.result;
}

async function walkJsonl(dir) {
  const out = [];
  async function walk(current) {
    let entries;
    try {
      entries = await readdir(current, { withFileTypes: true });
    } catch {
      return;

View on GitHub (pinned to a221052e0d)

Solutions

  1. Reproduce outside the script: `curl -sv <core-url> -X POST -H 'content-type: application/json' -d '{}'` and look at what answers
  2. Fix --core-url to the real core endpoint including /rpc (default http://127.0.0.1:7788/rpc)
  3. Set NO_PROXY/no_proxy for 127.0.0.1 and retry
  4. Confirm the core process is up: `curl -s http://127.0.0.1:7788/health`
  5. If the culprit server is stale, kill it or let --spawn-core pick a free port

Example fix

# before
node scripts/debug/harness-cache-audit.mjs --core-url http://127.0.0.1:5173/rpc
# Error: RPC core.ping returned non-JSON HTTP 200

# after
node scripts/debug/harness-cache-audit.mjs --core-url http://127.0.0.1:7788/rpc
Defensive patterns

Strategy: validation

Validate before calling

const probe = await fetch(coreUrl, { method: "POST", headers: { "content-type": "application/json" }, body: "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"core.ping\",\"params\":{}}" });
const text = await probe.text();
if (!probe.ok || !JSON.parse(text.slice(0, 1) ? text : "null")) {
  console.error(`${coreUrl} did not answer JSON (HTTP ${probe.status})`);
  process.exit(2);
}

Type guard

const isJsonBody = (text) => { try { JSON.parse(text); return true; } catch { return false; } };

Try / catch

let body;
try {
  body = JSON.parse(bodyText);
} catch {
  // enrich the bare script error with the body head — this script omits it
  throw new Error(`non-JSON HTTP ${res.status} from ${coreUrl}; head: ${bodyText.slice(0, 120)}`);
}

Prevention

When it happens

Trigger: --core-url or inherited OPENHUMAN_CORE_RPC_URL pointing at the Vite dev server (5173), a Docker proxy, or a cloud endpoint that returns HTML; missing /rpc path so the root handler answers; proxy env (HTTP_PROXY/HTTPS_PROXY) capturing the loopback fetch; the external core crashing between headers and body.

Common situations: Default URL http://127.0.0.1:7788/rpc occupied by a different service after a reboot; env exported in a shared dotfile for a remote core no longer running; VPN client intercepting loopback traffic.

Related errors


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