thedotmack/claude-mem · error · Error

Request timed out after

Error message

Request timed out after ${timeoutMs}ms

What it means

fetchWithTimeout wraps workerFetch with AbortSignal.timeout(timeoutMs). When the fetch aborts with a DOMException named 'TimeoutError', the DOMException (whose text is runtime-dependent) is normalized into the stable message 'Request timed out after <N>ms' so callers like hook-command.ts and server-beta-client.ts can match on the text reliably.

Solutions

  1. Check the worker is alive and responsive (`claude-mem status` / worker logs); restart it if hung
  2. Increase the timeoutMs passed to fetchWithTimeout/workerHttpRequest for heavy requests
  3. Add retry-with-backoff around the call for transient slowness, and match the stable 'Request timed out after' message in catch blocks

Example fix

// before
const res = await workerHttpRequest('/hooks', init, 1000); // too tight

// after
const res = await withRetry(() => workerHttpRequest('/hooks', init, 10000), { retries: 2 });
// catch (e) { if (e.message.includes('Request timed out after')) ... }
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

function isTimeoutError(e: unknown): boolean {
  return e instanceof Error && e.message.includes('Request timed out after');
}

Try / catch

try {
  return await fetchWithTimeout(url, init, timeoutMs);
} catch (e) {
  if (isTimeoutError(e)) {
    await sleep(backoffMs);
    return await fetchWithTimeout(url, init, timeoutMs * 2); // retry once with a larger budget
  }
  throw e;
}

Prevention

When it happens

Trigger: workerHttpRequest -> fetchWithTimeout with a timeoutMs deadline, and the worker HTTP request does not complete before AbortSignal.timeout fires: the worker process is hung, slow, or not draining its HTTP handler.

Common situations: claude-mem worker busy or deadlocked so requests queue; very short timeoutMs for a heavy hook request; machine under load or the Chroma service the worker proxies to is slow.

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.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/e51d63f56b3317a8. Report an issue: GitHub.

Appendix: source

Thrown at src/shared/worker-utils.ts:156

  } catch (err: unknown) {
    if (isWorkerFetchVerboseEnabled()) {
      logVerboseFetchFailure(url, init, err);
    }
    throw err;
  }
}

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 workerFetch(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 d8bc9755e7)