thedotmack/claude-mem · error · Error

Worker request timed out after ${WORKER_FETCH_TIMEOUT_MS}ms:

Error message

Worker request timed out after ${WORKER_FETCH_TIMEOUT_MS}ms: ${url}

What it means

Thrown by fetchWithTimeout() when the AbortController fires after WORKER_FETCH_TIMEOUT_MS (30000ms) and the resulting AbortError is re-interpreted as a worker timeout. The export script wraps every worker HTTP call in this 30-second bound, so any single request exceeding it produces this error rather than hanging indefinitely. Non-abort fetch errors are re-thrown unchanged.

Source

Thrown at scripts/export-memories.ts:42

  const port = Number.parseInt(normalized, 10);
  if (!Number.isInteger(port) || port < 1 || port > 65535 || String(port) !== normalized) {
    throw new Error(`Invalid CLAUDE_MEM_WORKER_PORT in settings.json: ${rawPort}`);
  }
  return port;
}

async function fetchWithTimeout(url: string, init?: RequestInit): Promise<Response> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), WORKER_FETCH_TIMEOUT_MS);

  try {
    return await fetch(url, {
      ...init,
      signal: controller.signal,
    });
  } catch (error) {
    if (error instanceof Error && error.name === 'AbortError') {
      throw new Error(`Worker request timed out after ${WORKER_FETCH_TIMEOUT_MS}ms: ${url}`);
    }
    throw error;
  } finally {
    clearTimeout(timeout);
  }
}

export async function exportMemories(query: string, outputFile: string, project?: string) {
  const settings = SettingsDefaultsManager.loadFromFile(join(resolveDataDir(), 'settings.json'));
  const port = parseWorkerPort(settings.CLAUDE_MEM_WORKER_PORT);
  const baseUrl = `http://localhost:${port}`;

  console.log(`🔍 Searching for: "${query}"${project ? ` (project: ${project})` : ' (all projects)'}`);

  const params = new URLSearchParams({
    query,
    format: 'json',
    limit: '999999'

View on GitHub (pinned to d768ba3643)

Solutions

  1. Narrow the export: pass a more specific query and/or --project to reduce the result set so /api/search returns within 30s.
  2. Check worker health (logs, CPU, DB connection pool) and restart it if wedged.
  3. Confirm the worker port is correct so the request isn't hitting a dead listener that never RSTs.
  4. If large exports are expected, raise WORKER_FETCH_TIMEOUT_MS in the script and rebuild, or paginate the export in smaller batches.

Example fix

// before — single huge export
exportMemories('""', 'all.json')
// after — scoped, returns fast
exportMemories('auth', 'auth.json', 'claude-mem')
Defensive patterns

Strategy: retry

Validate before calling

import { createConnection } from 'net';
const sock = createConnection({ host: '127.0.0.1', port }, () => { sock.end(); });
sock.on('error', () => { /* worker not listening */ });

Try / catch

try {
  return await fetchWithTimeout(url, init);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Worker request timed out')) {
    // narrow the query and retry once, or surface a 'reduce scope' message
  }
  throw e;
}

Prevention

When it happens

Trigger: Any fetchWithTimeout() call (search, sdk-sessions/batch) where the worker does not respond within 30s. Concretely: exporting a huge result set against /api/search with limit=999999, the worker blocked on a slow Postgres/Chroma query, the worker process wedged, or localhost loopback stalled.

Common situations: Large memory database where the hybrid search query plan is slow. Worker contending with the daemon for DB connections. A degenerate query that matches nearly every observation. Resource-constrained machine where the worker is swapping.

Understand the failure class

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/97f225be8be20630. Report an issue: GitHub.