{"record":{"id":"25ccc518453c85d2","repo":"thedotmack/claude-mem","slug":"server-was-already-stopped-when-close-was-requeste-25ccc5","errorCode":null,"errorMessage":"Server was already stopped when close was requested","messagePattern":"Server was already stopped when close was requested","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"src/services/infrastructure/GracefulShutdown.ts","lineNumber":81,"sourceCode":"  if (process.platform === 'win32') {\n    await new Promise(r => setTimeout(r, 500));\n  }\n\n  await new Promise<void>((resolve, reject) => {\n    server.close(err => {\n      if (!err) {\n        resolve();\n        return;\n      }\n      // #3380 — Node's http.Server.close(cb) reports ERR_SERVER_NOT_RUNNING\n      // when the handle is not listening (e.g. the bind failed or the server\n      // already closed). Closing an already-closed server is the desired end\n      // state, not a failure: rejecting here aborted ALL remaining teardown\n      // (session drain, MCP close, chroma stop, db close, supervisor stop).\n      // Same tolerance as ServerService.stop() in\n      // src/server/runtime/ServerService.ts.\n      if ((err as NodeJS.ErrnoException).code === 'ERR_SERVER_NOT_RUNNING') {\n        logger.warn('SYSTEM', 'Server was already stopped when close was requested', {}, err);\n        resolve();\n        return;\n      }\n      reject(err);\n    });\n  });\n\n  if (process.platform === 'win32') {\n    await new Promise(r => setTimeout(r, 500));\n    logger.info('SYSTEM', 'Waited for Windows port cleanup');\n  }\n}\n","sourceCodeStart":63,"sourceCodeEnd":94,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/infrastructure/GracefulShutdown.ts#L63-L94","documentation":"GracefulShutdown closes the HTTP server with server.close(cb). Node invokes the callback with ERR_SERVER_NOT_RUNNING when the handle was never listening or was already closed. Per issue #3380 this end state is treated as success: the warning is logged, the promise resolves, and the remaining teardown (session drain, MCP close, chroma stop, db close, supervisor stop) still runs — previously a reject here aborted all of it.","triggerScenarios":"close() is called on a server whose listen() failed earlier, or on a server another shutdown path already closed: double shutdown, restart flows, or a race between the monitor stopping the listener and the global shutdown cascade reaching it.","commonSituations":"Restart sequences that stop the worker twice; port-bind failure at startup followed by shutdown; teardown ordering where the HTTP server closes before the global shutdown begins.","solutions":["No action required — the message records an already-desired state and teardown continues.","If it appears alongside a startup bind error, fix the port conflict or EACCES at listen() time; that is the real failure.","Expect at most one occurrence per double-stop; repeated occurrences indicate a shutdown loop worth investigating."],"exampleFix":"// before: any close error aborted all remaining teardown\nserver.close(err => { if (err) reject(err); });\n// after: already-stopped is the desired end state, keep tearing down\nserver.close(err => {\n  if ((err as NodeJS.ErrnoException)?.code === 'ERR_SERVER_NOT_RUNNING') resolve();\n  else if (err) reject(err);\n  else resolve();\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isServerNotRunning(e: unknown): e is NodeJS.ErrnoException {\n  return (\n    typeof e === 'object' && e !== null &&\n    (e as NodeJS.ErrnoException).code === 'ERR_SERVER_NOT_RUNNING'\n  );\n}","tryCatchPattern":"await new Promise<void>((resolve, reject) => {\n  server.close(err => {\n    if (!err || (err as NodeJS.ErrnoException).code === 'ERR_SERVER_NOT_RUNNING') resolve();\n    else reject(err);\n  });\n});","preventionTips":["Treat close() on an already-closed server as success in every shutdown path.","Keep shutdown idempotent: one supervisor owns the teardown sequence.","Test restart flows for double-stop races."],"tags":["shutdown","http-server","nodejs","idempotency"],"backgroundTag":"server-not-running","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}