{"record":{"id":"323e66739fd15152","repo":"thedotmack/claude-mem","slug":"shutdown-request-returned-error","errorCode":null,"errorMessage":"Shutdown request returned error","messagePattern":"Shutdown request returned error","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/infrastructure/HealthMonitor.ts","lineNumber":134,"sourceCode":"\nexport async function waitForPortFree(port: number, timeoutMs: number = 10000): Promise<boolean> {\n  const start = Date.now();\n  while (Date.now() - start < timeoutMs) {\n    if (!(await isPortInUse(port))) return true;\n    await new Promise(r => setTimeout(r, 500));\n  }\n  return false;\n}\n\nexport async function httpShutdown(port: number, reason: 'stop' | 'restart' = 'stop'): Promise<boolean> {\n  try {\n    // The CLI restart path stops the worker through this same endpoint; the\n    // reason tag lets the worker report shutdown_reason: 'restart' on its\n    // worker_stopped telemetry instead of a generic 'stop'.\n    const endpointPath = reason === 'restart' ? '/api/admin/shutdown?reason=restart' : '/api/admin/shutdown';\n    const result = await httpRequestToWorker(port, endpointPath, 'POST');\n    if (!result.ok) {\n      logger.warn('SYSTEM', 'Shutdown request returned error', { status: result.statusCode });\n      return false;\n    }\n    return true;\n  } catch (error) {\n    if (error instanceof Error && error.message?.includes('ECONNREFUSED')) {\n      logger.debug('SYSTEM', 'Worker already stopped', {}, error);\n      return false;\n    }\n    logger.error('SYSTEM', 'Shutdown request failed unexpectedly', {}, error as Error);\n    return false;\n  }\n}\n\nexport async function getRunningWorkerVersion(port: number): Promise<string | null> {\n  try {\n    const result = await httpRequestToWorker(port, '/api/health');\n    if (!result.ok) return null;\n    const data = JSON.parse(result.body) as { version: string };","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/infrastructure/HealthMonitor.ts#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the logged status: 404 → version/endpoint mismatch, 401/403 → auth mismatch, 500 → the worker's shutdown handler failed (check worker logs).","Verify CLI and worker come from the same claude-mem version (upgrade/downgrade so endpoint names match).","Align admin auth configuration (token env/config) between the caller and the worker.","If HTTP shutdown keeps failing, use the PID-based stop path (ProcessManager) as the documented fallback."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// confirm the worker is listening and the endpoint exists before posting\nconst health = await httpRequestToWorker(port, '/api/health', 'GET');\nif (!health.ok) {\n  throw new Error(`worker not reachable or wrong version on port ${port}`);\n}\nconst ok = await httpShutdown(port, 'restart');","typeGuard":null,"tryCatchPattern":"try {\n  const ok = await httpShutdown(port, 'restart');\n  if (!ok) await stopByPid(); // documented signal-based fallback\n} catch (e) {\n  if (e instanceof Error && e.message.includes('ECONNREFUSED')) {\n    // worker already gone: treat as stopped\n  } else throw e;\n}","preventionTips":["Keep CLI and worker on the same claude-mem version.","Align admin auth tokens between caller and worker.","Always implement the PID-based stop as the fallback when HTTP shutdown returns false."],"tags":["shutdown","http","admin-api","worker"],"backgroundTag":"http-error-response","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}