thedotmack/claude-mem · warning

Shutdown request returned error

Error message

Shutdown request returned error

What it means

httpShutdown POSTs to the worker's /api/admin/shutdown endpoint (with ?reason=restart on the restart path) to stop or restart it. This warning means the HTTP round trip completed but the endpoint answered with a non-OK status, so the worker did not acknowledge shutdown through this path. The function returns false, letting callers fall back to PID/signal-based termination.

Source

Thrown at src/services/infrastructure/HealthMonitor.ts:134

export async function waitForPortFree(port: number, timeoutMs: number = 10000): Promise<boolean> {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    if (!(await isPortInUse(port))) return true;
    await new Promise(r => setTimeout(r, 500));
  }
  return false;
}

export async function httpShutdown(port: number, reason: 'stop' | 'restart' = 'stop'): Promise<boolean> {
  try {
    // The CLI restart path stops the worker through this same endpoint; the
    // reason tag lets the worker report shutdown_reason: 'restart' on its
    // worker_stopped telemetry instead of a generic 'stop'.
    const endpointPath = reason === 'restart' ? '/api/admin/shutdown?reason=restart' : '/api/admin/shutdown';
    const result = await httpRequestToWorker(port, endpointPath, 'POST');
    if (!result.ok) {
      logger.warn('SYSTEM', 'Shutdown request returned error', { status: result.statusCode });
      return false;
    }
    return true;
  } catch (error) {
    if (error instanceof Error && error.message?.includes('ECONNREFUSED')) {
      logger.debug('SYSTEM', 'Worker already stopped', {}, error);
      return false;
    }
    logger.error('SYSTEM', 'Shutdown request failed unexpectedly', {}, error as Error);
    return false;
  }
}

export async function getRunningWorkerVersion(port: number): Promise<string | null> {
  try {
    const result = await httpRequestToWorker(port, '/api/health');
    if (!result.ok) return null;
    const data = JSON.parse(result.body) as { version: string };

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Read the logged status: 404 → version/endpoint mismatch, 401/403 → auth mismatch, 500 → the worker's shutdown handler failed (check worker logs).
  2. Verify CLI and worker come from the same claude-mem version (upgrade/downgrade so endpoint names match).
  3. Align admin auth configuration (token env/config) between the caller and the worker.
  4. If HTTP shutdown keeps failing, use the PID-based stop path (ProcessManager) as the documented fallback.
Defensive patterns

Strategy: retry

Validate before calling

// confirm the worker is listening and the endpoint exists before posting
const health = await httpRequestToWorker(port, '/api/health', 'GET');
if (!health.ok) {
  throw new Error(`worker not reachable or wrong version on port ${port}`);
}
const ok = await httpShutdown(port, 'restart');

Try / catch

try {
  const ok = await httpShutdown(port, 'restart');
  if (!ok) await stopByPid(); // documented signal-based fallback
} catch (e) {
  if (e instanceof Error && e.message.includes('ECONNREFUSED')) {
    // worker already gone: treat as stopped
  } else throw e;
}

Prevention

When it happens

Trigger: POST /api/admin/shutdown returns 4xx/5xx: 401/403 when auth blocks the admin route, 404 when the CLI and worker versions disagree on the endpoint's existence, 500 when the worker's shutdown handler itself crashes.

Common situations: CLI `claude-mem restart` against an older or newer worker with a changed admin API; admin auth token mismatch between CLI and worker; a reverse proxy in front of the worker rewriting or blocking the route.

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


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/323e66739fd15152. Report an issue: GitHub.