tinyhumansai/openhuman · error · CoreRpcError
Core RPC ${payload.method} timed out after ${effectiveTimeou
Error message
Core RPC ${payload.method} timed out after ${effectiveTimeoutMs}ms What it means
A CoreRpcError with kind 'timeout' thrown by callCoreRpc: every RPC is bounded by an AbortController + setTimeout (manually wired so fake timers work in tests), with a per-call timeoutMs clamped into effectiveTimeoutMs. When the fetch aborts before the core answers, the abort is detected in the catch and re-thrown as this classified error so callers and Sentry filters can branch on err.kind === 'timeout'.
Source
Thrown at app/src/services/coreRpcClient.ts:726
JSON.stringify(payload),
controller.signal
);
} else {
response = await fetch(rpcUrl, {
method: 'POST',
headers,
body: JSON.stringify(payload),
signal: controller.signal,
});
}
} catch (fetchErr) {
if (controller.signal.aborted) {
// Throw a fully-classified `CoreRpcError` here so the outer catch
// doesn't re-wrap a bare `Error` and so callers can branch on
// `err.kind === 'timeout'` (Sentry filter, soft toast skip). Use
// the per-call `effectiveTimeoutMs` so the message reflects the
// actual budget (#2156 raised the snapshot path to 90s).
throw new CoreRpcError(
`Core RPC ${payload.method} timed out after ${effectiveTimeoutMs}ms`,
'timeout'
);
}
throw fetchErr;
} finally {
clearTimeout(timeoutId);
}
if (!response.ok) {
const text = await response.text();
const httpMessage = `Core RPC HTTP ${response.status}: ${text || response.statusText}`;
const kind = classifyRpcError(text || response.statusText, response.status);
if (kind === 'auth_expired' && !suppressAuthExpiredEvent)
dispatchAuthExpired(
payload.method,
classifyAuthExpiredReason(text || response.statusText, response.status)
);View on GitHub (pinned to a221052e0d)
Solutions
- For known-slow methods, pass a larger per-call budget: callCoreRpc({ method, params, timeoutMs: 90_000 })
- Retry once — transient core congestion often clears; treat kind === 'timeout' as retryable (the analytics module already special-cases it)
- Restart the core if it is consistently timing out (possible deadlock) and check core CPU / logs around the timeouts
- If you are adding a legitimately slow RPC, budget its timeoutMs like #2156 did rather than letting it hit the default
Example fix
// before
await callCoreRpc({ method: 'openhuman.app_state_snapshot' });
// after
await callCoreRpc({ method: 'openhuman.app_state_snapshot', timeoutMs: 90_000 }); Defensive patterns
Strategy: retry
Validate before calling
// Budget slow methods explicitly instead of relying on the default
await callCoreRpc({ method: 'openhuman.app_state_snapshot', timeoutMs: 90_000 }); Type guard
import { CoreRpcError } from '../services/coreRpcClient';
function isRpcTimeout(e: unknown): e is CoreRpcError {
return e instanceof CoreRpcError && e.kind === 'timeout';
} Try / catch
try {
return await callCoreRpc(payload);
} catch (e) {
if (isRpcTimeout(e)) {
await delay(1000);
return callCoreRpc(payload); // one bounded retry; report if it repeats
}
throw e;
} Prevention
- Pass a generous timeoutMs for cold-start and known-slow methods (see #2156)
- Branch on err.kind === 'timeout' — Sentry/toast filters already special-case it
- Consistent timeouts across methods usually mean a wedged core: restart rather than retry forever
When it happens
Trigger: The core is slow or hung: first-launch app_state_snapshot on a cold workspace (this path was explicitly raised to 90s in #2156), long config migrations at boot, a deadlocked or CPU-pinned core, or a genuinely slow method called with the default budget.
Common situations: Cold start with a large workspace/migration; core stuck on a lock (store contention); heavy operations (memory indexing) starving the RPC thread pool; slower hardware stretching slow calls past the default budget.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- RPC ${method} timed out after ${timeoutMs}ms
- RPC ${method} timed out after ${timeoutMs}ms
- RPC ${method} timed out after ${timeoutMs}ms
- timed out waiting for spawned core at ${coreUrl} ${stderrFn(
- Invalid ${paramName}: '${value}'. Must be a valid integer ID
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/689c13cad640a0a6.
Report an issue: GitHub.