{"record":{"id":"26e796a229fc62c6","repo":"thedotmack/claude-mem","slug":"shutdown-already-in-progress-ignoring-re-entrant","errorCode":null,"errorMessage":"Shutdown already in progress — ignoring re-entrant shutdown request","messagePattern":"Shutdown already in progress — ignoring re-entrant shutdown request","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/worker-shutdown.ts","lineNumber":66,"sourceCode":"  removePidFile: () => void;\n  spawnDaemon: (scriptPath: string, port: number) => number | undefined;\n}\n\nexport interface ShutdownSequenceOptions {\n  reason: WorkerShutdownReason;\n  /** Reads the owner's shutdown flag (WorkerService.isShuttingDown). */\n  isShuttingDown: () => boolean;\n  markShuttingDown: () => void;\n  /** Pre-graceful bookkeeping: transcript watcher, heartbeat, sentinel, telemetry flush. */\n  beforeGracefulShutdown: () => Promise<void>;\n  performGracefulShutdown: () => Promise<void>;\n  gracefulDeadlineMs: number;\n  restartHandoff: RestartHandoffDeps;\n}\n\nexport async function runShutdownSequence(options: ShutdownSequenceOptions): Promise<void> {\n  if (options.isShuttingDown()) {\n    logger.warn('SYSTEM', 'Shutdown already in progress — ignoring re-entrant shutdown request', {\n      reason: options.reason,\n    });\n    return;\n  }\n  options.markShuttingDown();\n\n  try {\n    await options.beforeGracefulShutdown();\n  } catch (error: unknown) {\n    // Pre-graceful bookkeeping (watcher/heartbeat/sentinel/telemetry flush)\n    // failing must not abort the sequence: graceful shutdown and — for\n    // restarts — the successor handoff still have to run. Same \"proceed on\n    // error, never abort the handoff\" policy as performGracefulShutdown below.\n    logger.error(\n      'SYSTEM',\n      'Pre-graceful shutdown bookkeeping failed — proceeding',\n      { reason: options.reason },\n      error instanceof Error ? error : new Error(String(error))","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/worker-shutdown.ts#L48-L84","documentation":"The shutdown sequence is guarded by a re-entrancy flag (WorkerService.isShuttingDown). A second shutdown request — another signal, a CLI stop, or a restart — arriving while a shutdown is already running logs this warning and returns immediately, so teardown steps cannot interleave or run twice.","triggerScenarios":"Pressing Ctrl+C twice; sending SIGTERM followed by SIGINT; running `claude-mem stop` while a supervisor-initiated shutdown is in flight; a restart racing a signal.","commonSituations":"Impatient double Ctrl+C in a terminal; process managers (systemd, pm2, docker stop) that send multiple signals; test harnesses that trigger shutdown from several code paths.","solutions":["Treat it as benign — the first shutdown continues; just wait for process exit","If the process never exits afterwards, the first shutdown is hung — inspect the graceful-shutdown deadline warning and hanging resources","Configure supervisors to send one signal and wait (e.g. docker StopSignal + generous stop timeout)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"async function shutdownOnce(worker: WorkerService, reason: string): Promise<void> {\n  if (worker.isShuttingDown) return; // idempotent guard on the caller side too\n  await worker.shutdown(reason);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send a single shutdown signal and wait; suppress duplicate signals in wrappers","Configure supervisors (docker/pm2/systemd) with adequate stop timeouts so the first signal can finish","Treat the warning as confirmation the guard worked, not as a fault to fix"],"tags":["shutdown","signals","reentrancy","worker"],"backgroundTag":"double-shutdown-signal","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}