thedotmack/claude-mem · warning
Request timed out after ${timeoutMs}ms
Error message
Request timed out after ${timeoutMs}ms What it means
Thrown by fetchWithTimeout when AbortSignal.timeout fires before the fetch completes. The DOMException TimeoutError is normalized to a plain Error with the historical 'Request timed out after Nms' message that callers (hook-command.ts, server-beta-client.ts) string-match on. Non-timeout fetch errors are rethrown unchanged.
Source
Thrown at src/shared/worker-utils.ts:61
const HOOK_READINESS_TIMEOUT_MS = readTimeoutEnv(
'CLAUDE_MEM_HOOK_READINESS_TIMEOUT_MS',
getTimeout(HOOK_TIMEOUTS.HOOK_READINESS_WAIT),
{ min: 0, max: 300000 }
);
const API_REQUEST_TIMEOUT_BOUNDS = { min: 500, max: 300000 } as const;
export async function fetchWithTimeout(url: string, init: RequestInit = {}, timeoutMs: number): Promise<Response> {
try {
// AbortSignal.timeout (Node 18+) replaces the manual setTimeout/clearTimeout
// race. On expiry it aborts with a TimeoutError DOMException.
return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
} catch (err: unknown) {
// Preserve the historical timeout-error message ("...timed out...") that
// callers match on (hook-command.ts, server-beta-client.ts) — the
// DOMException text is runtime-dependent, so normalize it here.
if (err instanceof DOMException && err.name === 'TimeoutError') {
throw new Error(`Request timed out after ${timeoutMs}ms`);
}
throw err;
}
}
let cachedPort: number | null = null;
let cachedHost: string | null = null;
let cachedSettings: SettingsDefaults | null = null;
let cachedApiRequestTimeoutMs: number | null = null;
function getWorkerSettingsPath(): string {
return path.join(SettingsDefaultsManager.get('CLAUDE_MEM_DATA_DIR'), 'settings.json');
}
function getWorkerSettings(): SettingsDefaults {
if (cachedSettings !== null) {
return cachedSettings;
}View on GitHub (pinned to d768ba3643)
Solutions
- Confirm the target worker/server is running and responsive (health check).
- Raise the configured timeout (within bounds 500–300000ms) if the operation legitimately needs more time.
- For transient slowness, retry with backoff; treat this as a recoverable network error.
- If the worker is wedged, restart it and re-issue the request.
Example fix
// before
const res = await fetchWithTimeout(url, {}, 1000);
// after
const res = await fetchWithTimeout(url, {}, 10_000);
// or retry on timeout
for (let attempt = 0; attempt < 3; attempt++) {
try { return await fetchWithTimeout(url, {}, 10_000); }
catch (e) { if (!/timed out/.test(e.message) || attempt === 2) throw e; }
} Defensive patterns
Strategy: retry
Validate before calling
// Validate the worker is reachable before the timed request
async function workerHealthy(url: string): Promise<boolean> {
try { const r = await fetch(`${url}/health`, { signal: AbortSignal.timeout(2000) }); return r.ok; }
catch { return false; }
}
if (!await workerHealthy(url)) throw new Error('Worker unreachable before request'); Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await fetchWithTimeout(url, init, timeoutMs);
} catch (err) {
const isTimeout = err instanceof Error && /timed out/.test(err.message);
if (!isTimeout || attempt === 2) throw err;
await new Promise(r => setTimeout(r, 250 * 2 ** attempt));
}
} Prevention
- Size timeoutMs to the operation (within 500–300000ms bounds).
- Health-check the worker before issuing long requests.
- Treat 'Request timed out' as retryable with backoff for transient slowness.
When it happens
Trigger: A fetch(url, init) within fetchWithTimeout exceeds the passed timeoutMs; AbortSignal.timeout aborts with a TimeoutError DOMException, caught and rewrapped.
Common situations: Worker server slow or unreachable; oversized response body; network latency; the API_REQUEST_TIMEOUT_MS configured too low for the operation; the worker process hung/dead.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Worker request timed out after ${WORKER_FETCH_TIMEOUT_MS}ms:
- ${timeoutMessage} (timed out after ${timeoutMs}ms)
- SSE stream returned HTTP ${response.status}
- Failed to get processing status: ${res.status}
- Failed to trigger processing: ${res.status}
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/e51d63f56b3317a8.
Report an issue: GitHub.