tinyhumansai/openhuman · error · Error

RPC ${method} timed out after ${timeoutMs}ms

Error message

RPC ${method} timed out after ${timeoutMs}ms

What it means

rpc() in goals-live.mjs wraps every JSON-RPC POST in an AbortController with a per-call timeout (default 600000 ms from --rpc-timeout-ms, 10 s for the internal core.ping probe). When the abort fires, fetch rejects with an AbortError which is translated into this timeout message naming the method and the limit. It exists so a hung core or a slow model provider cannot stall the runner indefinitely.

Source

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

  let res;
  try {
    res = await fetch(coreUrl, {
      method: "POST",
      signal: controller.signal,
      headers: {
        "content-type": "application/json",
        authorization: `Bearer ${token}`,
      },
      body: JSON.stringify({
        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 }.

View on GitHub (pinned to a221052e0d)

Solutions

  1. Raise the limit: `--rpc-timeout-ms 1200000` (20 min) or higher for reflect/enrichment turns
  2. Retry the run — provider-side stalls are often transient
  3. Use a faster model via --model, or run only the cheap cases (`--case list --case add`) while iterating
  4. Check core health/load (GET /health, process CPU) to distinguish a hung core from a slow provider
  5. If it happens on every call including core.ping, the core is wedged: restart it (or rerun with --spawn-core for a fresh instance)

Example fix

// before
node scripts/debug/goals-live.mjs --case reflect --rpc-timeout-ms 60000
# Error: RPC openhuman.memory_goals_reflect timed out after 60000ms

// after
node scripts/debug/goals-live.mjs --case reflect --rpc-timeout-ms 900000
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: confirm the core answers a cheap ping within a small budget
// before committing to a long, expensive audit run
try {
  await rpc(coreUrl, token, "core.ping", {}, 5_000);
} catch {
  console.error("core not answering core.ping within 5s — fix connectivity before the audit");
  process.exit(2);
}

Type guard

const isTimeoutError = (err) => /timed out after \d+ms$/.test(err?.message || "");

Try / catch

for (const timeoutMs of [600_000, 1_200_000]) {
  try {
    return await rpc(coreUrl, token, method, params, timeoutMs);
  } catch (err) {
    if (!/timed out after/.test(err.message) || timeoutMs === 1_200_000) throw err;
  }
}

Prevention

When it happens

Trigger: A goals reflect turn (openhuman.memory_goals_reflect driving the goals_agent) whose LLM inference exceeds --rpc-timeout-ms; a heavily loaded or deadlocked core; provider-side latency (rate limits, retries) pushing a single agent_chat-class call past the limit; setting --rpc-timeout-ms too low (e.g. 30000) for enrichment turns.

Common situations: Default 10-minute timeout exceeded on slow/cheap models or long prompts; timeout knob lowered for quick tests and then reused for the expensive reflect case; core busy compiling or serving other requests; network egress to the inference provider throttled.

Understand the failure class

Related errors


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