tinyhumansai/openhuman · error
[transport:local] timed out after ms
Error message
[transport:local] ${method} timed out after ${this.timeoutMs}ms What it means
LocalTransport arms a per-call AbortController with setTimeout(timeoutMs) (default 30_000ms, constructor third argument) and merges it with the caller's own opts.signal. If fetch rejects and controller.signal.aborted is true, it reports this timeout error. Note the guard checks only controller.signal.aborted, so a caller-initiated abort inside the window is also reported as a timeout.
Solutions
- Construct LocalTransport with a larger timeoutMs for known-slow operations (pass 120_000 as the third constructor arg)
- Check core liveness first: GET /health on the same rpcUrl origin — if that also hangs, restart the core process rather than raising the timeout
- For chat/agent calls, prefer a streaming method so no single call must finish within one window
- If you pass opts.signal yourself, make sure you are not aborting it (the message cannot distinguish your abort from the timeout)
Example fix
// before const t = new LocalTransport(getRpcUrl, getRpcToken); // after const t = new LocalTransport(getRpcUrl, getRpcToken, 120_000);
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight liveness so you only pay the long timeout for real work:
async function coreAlive(rpcUrl: string): Promise<boolean> {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), 2_000);
try {
const res = await fetch(rpcUrl, { method: 'GET', signal: ctrl.signal });
return res.ok || res.status === 404; // listener is up even on unknown GET routes
} catch { return false; } finally { clearTimeout(t); }
} Type guard
function isTimeoutError(err: unknown): boolean {
return err instanceof Error && err.message.includes('timed out after');
} Try / catch
try {
return await local.call(method, params);
} catch (err) {
if (isTimeoutError(err)) {
await sleep(1_000);
return await local.call(method, params); // one retry; upgrade timeout if it repeats
}
throw err;
} Prevention
- Size timeoutMs per call class: keep 30s for pings/gets, use 120s+ for agent/memory methods
- Do not pass your own AbortSignal unless you handle its abort distinctly — the transport reports both as timeout
- Pre-flight with a cheap ping when a stall is suspected instead of waiting out the full window
When it happens
Trigger: Any openhuman.* RPC whose server-side work exceeds timeoutMs: long agent turns, memory sync/embedding backfills, flow runs; a hung or paused core process; debugger breakpoints freezing the core; or the caller aborting its signal mid-call (conflated into the same message).
Common situations: The 30s default is too short for LLM-bound methods (chat turns, memory consolidation) on slow local models; core is under CPU load or swapping; core restarted while a request was in flight.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Core RPC returned an error
- [transport:local] HTTP
- [transport:local] response missing result
- Cloud RPC returned an error
- LAN RPC returned an error
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/5f96e4bb297fb5e4.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/services/transport/LocalTransport.ts:68
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
// Merge caller signal with timeout signal.
opts?.signal?.addEventListener('abort', () => controller.abort());
let response: Response;
try {
response = await fetch(rpcUrl, {
method: 'POST',
headers,
body: JSON.stringify(payload),
signal: controller.signal,
});
} catch (err) {
if (controller.signal.aborted) {
throw new Error(`[transport:local] ${method} timed out after ${this.timeoutMs}ms`);
}
throw err;
} finally {
clearTimeout(timeoutId);
}
if (!response.ok) {
const text = await response.text();
throw new Error(`[transport:local] HTTP ${response.status}: ${text || response.statusText}`);
}
const json = (await response.json()) as JsonRpcResponse<T>;
if (json.error) {
logErr('[transport:local] ← %s error: %s', method, json.error.message);
throw new Error(json.error.message ?? 'Core RPC returned an error');
}
if (!Object.prototype.hasOwnProperty.call(json, 'result')) {View on GitHub (pinned to 7491200858)