thedotmack/claude-mem · error
Worker API error ( )
Error message
Worker API error (${response.status}): ${errorText} What it means
callWorker is the MCP server's HTTP client to the local claude-mem worker API. When the worker responds with a non-2xx status, it reads the response body and throws `Worker API error (<status>): <body>` so the underlying reason (404, 500, validation text, etc.) is preserved in the message. The throw is caught inside callWorker and surfaced to the MCP client as an isError tool result with the same text, so callers usually see it as an error-content response rather than an uncaught exception.
Solutions
- Inspect the status and body text after 'Worker API error' — it contains the worker's own error reason.
- Restart the worker: run `claude-mem status` / restart the plugin so ensureWorkerStarted respawns a fresh worker matching the current build.
- Check the worker is listening on the expected port (getWorkerPort) and that no stale process occupies it; kill leftover worker processes and retry.
- If the status is 404 after an upgrade, align MCP server and worker versions (rebuild/reinstall the plugin).
- Retry the tool call once the worker responds; the error is transient in most crash/staleness cases.
Defensive patterns
Strategy: retry
Validate before calling
// Probe worker availability before issuing the real call
const health = await fetch(`http://127.0.0.1:${getWorkerPort()}/health`).catch(() => null);
if (!health || !health.ok) throw new Error('worker not ready; restart claude-mem worker'); Try / catch
try {
return await callWorker('/api/search', { query: { q } });
} catch (err) {
const msg = (err as Error).message;
const m = msg.match(/Worker API error \((\d+)\)/);
if (m) {
const status = Number(m[1]);
if (status >= 500 || status === 503) await ensureWorkerStarted(); // respawn and retry once
}
return { content: [{ type: 'text', text: `worker call failed: ${msg}` }], isError: true };
} Prevention
- Restart/rebuild the worker whenever the plugin version changes to avoid MCP-server/worker API skew.
- Monitor for stale worker processes on the configured port and kill leftovers before restarting.
- Add a health-check ping before batch tool operations.
- Log the status + body from the error message; the worker's own text usually pinpoints the failing endpoint.
When it happens
Trigger: Any tool handler calling callWorker (e.g. handleSessionStartContext, search/timeline tools) while the worker HTTP endpoint returns non-ok: worker not fully started, worker crashed mid-request, unknown endpoint path, worker port mismatch, or the worker returning 4xx/5xx for the given query/body.
Common situations: Stale worker process from a previous plugin version still bound to the port; worker killed by OOM or machine sleep so requests hit a dead socket or half-updated route; MCP server and worker version skew after an upgrade leaving an endpoint the running worker doesn't implement; malformed tool arguments producing a 400/500 from the worker.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- [claude-mem] Worker GET
- [claude-mem] Worker POST
- [claude-mem] Worker GET
- Failed to clear logs
- Failed to fetch logs
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/98d88f35b6a50a06.
Report an issue: GitHub.
Appendix: source
Thrown at src/servers/mcp-server.ts:99
if (opts.body) {
response = await workerHttpRequest(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(opts.body)
});
} else {
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(opts.query ?? {})) {
if (value !== undefined && value !== null) {
searchParams.append(key, String(value));
}
}
response = await workerHttpRequest(`${endpoint}?${searchParams}`);
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Worker API error (${response.status}): ${errorText}`);
}
logger.debug('SYSTEM', '← Worker API success', undefined, { endpoint });
if (opts.text) {
return { content: [{ type: 'text' as const, text: await response.text() }] };
}
if (opts.body) {
return { content: [{ type: 'text' as const, text: JSON.stringify(await response.json(), null, 2) }] };
}
return await response.json() as { content: Array<{ type: 'text'; text: string }>; isError?: boolean };
} catch (error: unknown) {
logger.error('SYSTEM', '← Worker API error', { endpoint }, error instanceof Error ? error : new Error(String(error)));
return {
content: [{
type: 'text' as const,
text: `Error calling Worker API: ${error instanceof Error ? error.message : String(error)}`
}],View on GitHub (pinned to d8bc9755e7)