thedotmack/claude-mem · error · Error

Failed to search: ${searchResponse.status} ${searchResponse.

Error message

Failed to search: ${searchResponse.status} ${searchResponse.statusText}

What it means

Thrown after the GET to /api/search returns a non-2xx status. fetchWithTimeout already handled transport-level failures (timeouts, DNS), so reaching this branch means the worker answered but refused/errored on the search request. The message includes the status code and statusText for triage.

Source

Thrown at scripts/export-memories.ts:67

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'
  });
  if (project) params.set('project', project);

  console.log('📡 Fetching all memories via hybrid search...');
  const searchResponse = await fetchWithTimeout(`${baseUrl}/api/search?${params.toString()}`);
  if (!searchResponse.ok) {
    throw new Error(`Failed to search: ${searchResponse.status} ${searchResponse.statusText}`);
  }
  const searchData = await searchResponse.json();

  const observations: ObservationRecord[] = searchData.observations || [];
  const summaries: SessionSummaryRecord[] = searchData.sessions || [];
  const prompts: UserPromptRecord[] = searchData.prompts || [];

  console.log(`✅ Found ${observations.length} observations`);
  console.log(`✅ Found ${summaries.length} session summaries`);
  console.log(`✅ Found ${prompts.length} user prompts`);

  const memorySessionIds = new Set<string>();
  observations.forEach((o) => {
    if (o.memory_session_id) memorySessionIds.add(o.memory_session_id);
  });
  summaries.forEach((s) => {
    if (s.memory_session_id) memorySessionIds.add(s.memory_session_id);
  });

View on GitHub (pinned to d768ba3643)

Solutions

  1. Confirm the port matches the running worker (check the worker startup log) so the request reaches the right process.
  2. Read the worker logs for the matching 4xx/5xx to find the underlying cause (DB, Chroma, migration).
  3. If status is 404, upgrade/restart the worker so its route table matches this script's expected endpoints.
  4. If status is 500, run the worker's DB/migration setup and retry.
Defensive patterns

Strategy: try-catch

Validate before calling

const probe = await fetchWithTimeout(`${baseUrl}/api/health`);
if (!probe.ok) throw new Error(`Worker not healthy: ${probe.status}`);

Try / catch

try {
  const r = await fetchWithTimeout(`${baseUrl}/api/search?${params}`);
  if (!r.ok) throw new Error(`search ${r.status}`);
} catch (e) {
  if (e instanceof Error && /Failed to search/.test(e.message)) {
    // inspect status: 404 -> wrong port/version; 500 -> backend
  }
  throw e;
}

Prevention

When it happens

Trigger: The worker returns 400 (bad query params), 404 (wrong port — a different service answering, or the route is absent in this worker version), 500 (search backend crash — Postgres or Chroma), or 503 (worker not ready). Also any non-allowed code from a reverse proxy in front of the worker.

Common situations: CLAUDE_MEM_WORKER_PORT points at a stale/wrong process. The worker binary is an older version without /api/search. The vector/Chroma backend is misconfigured and the hybrid search path throws 500. The data dir is locked or the DB not migrated.

Related errors


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