thedotmack/claude-mem · error · ServerClientError
Server ${method} ${path} failed: ${message}
Error message
Server ${method} ${path} failed: ${message} What it means
Thrown by ServerClient.request when `fetchWithTimeout` itself throws — the request never got an HTTP response. The client inspects the underlying message: if it matches /timed out|timeout/i the kind is `timeout`, otherwise `transport` (DNS failure, ECONNREFUSED, TLS certificate error, socket reset). Both kinds are fallback-eligible, and `cause` carries the original error for diagnosis.
Source
Thrown at src/services/hooks/server-client.ts:373
const url = `${this.baseUrl}${path}`;
const init: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
},
};
if (body !== undefined) {
init.body = JSON.stringify(body);
}
let response: Response;
try {
response = await fetchWithTimeout(url, init, this.timeoutMs);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
const isTimeout = /timed out|timeout/i.test(message);
throw new ServerClientError(
isTimeout ? 'timeout' : 'transport',
`Server ${method} ${path} failed: ${message}`,
{ cause: error },
);
}
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new ServerClientError(
'http_error',
`Server ${method} ${path} returned ${response.status}: ${truncate(text, 200)}`,
{ status: response.status },
);
}
const text = await response.text();
if (!text || text.length === 0) {
// Endpoints we call always return JSON; a body-less success is unusualView on GitHub (pinned to e2d1df569a)
Solutions
- Verify the server is up and the base URL is right: `curl -i $BASE_URL/v1/health` (or any known route) from the same environment the hooks run in
- For ECONNREFUSED, start the server process / fix host and port in the client configuration
- For timeout kind, raise the client `timeoutMs` (or HOOK_TIMEOUTS) only after confirming the server is genuinely slow, and check server-side logs for the stall
- For TLS failures, fix the certificate or point baseUrl at a URL whose cert validates; as a stopgap honor the fallback path rather than disabling verification
- Because transport/timeout are fallback-eligible, catch ServerClientError and degrade to local operation instead of failing the hook
Example fix
// before
await client.recordEvent(input); // Server POST /v1/events failed: fetch failed
// after
try {
await client.recordEvent(input);
} catch (e) {
if (isServerClientError(e) && (e.kind === 'transport' || e.kind === 'timeout')) {
await fallbackToLocal(input); // fallback-eligible by design
} else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
import { request } from 'undici';
async function serverReachable(baseUrl: string): Promise<boolean> {
try { await request(baseUrl, { method: 'HEAD', headersTimeout: 2000 }); return true; }
catch { return false; }
}
if (!(await serverReachable(baseUrl))) throw new Error(`claude-mem server unreachable at ${baseUrl}`); Type guard
function isTransientServerError(e: unknown): boolean {
return isServerClientError(e) && (e.kind === 'transport' || e.kind === 'timeout');
} Try / catch
for (let attempt = 1; attempt <= 3; attempt++) {
try {
return await client.recordEvent(input);
} catch (e) {
if (!isServerClientError(e) || (e.kind !== 'transport' && e.kind !== 'timeout')) throw e;
if (attempt === 3) return fallbackToLocal(input); // fallback-eligible kinds
await sleep(250 * 2 ** (attempt - 1));
}
} Prevention
- Health-check the server URL at startup so misconfiguration fails loudly before real work
- Retry only transport/timeout kinds with backoff; http_error needs cause-specific handling
- Keep server and hooks on the same host/network plan, and pin the base URL in config rather than composing it ad hoc
When it happens
Trigger: The configured server base URL is unreachable: server process not running (ECONNREFUSED), wrong host/port in the URL, DNS name not resolving, self-signed/mismatched TLS cert, or the request exceeding `timeoutMs` (default from HOOK_TIMEOUTS.API_REQUEST). Also proxies/firewalls resetting the connection.
Common situations: Hooks configured for server mode while the claude-mem server is down or was never started; base URL pointing at localhost from a container where the server runs on another host; corporate MITM proxies breaking TLS; slow database making the server exceed the hook timeout.
Related errors
- [claude-mem] Worker POST ${path} failed: ${message}
- [claude-mem] Worker GET ${path} failed: ${message}
- Unknown Claude model: ${options.model}. Allowed: ${[...allow
- Failed to install Bun. Please install manually: ${manualInst
- invalid_response
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/769c7ed13b6402b4.
Report an issue: GitHub.