thedotmack/claude-mem · warning

Shutdown already in progress — ignoring re-entrant shutdown

Error message

Shutdown already in progress — ignoring re-entrant shutdown request

What it means

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.

Source

Thrown at src/services/worker-shutdown.ts:66

  removePidFile: () => void;
  spawnDaemon: (scriptPath: string, port: number) => number | undefined;
}

export interface ShutdownSequenceOptions {
  reason: WorkerShutdownReason;
  /** Reads the owner's shutdown flag (WorkerService.isShuttingDown). */
  isShuttingDown: () => boolean;
  markShuttingDown: () => void;
  /** Pre-graceful bookkeeping: transcript watcher, heartbeat, sentinel, telemetry flush. */
  beforeGracefulShutdown: () => Promise<void>;
  performGracefulShutdown: () => Promise<void>;
  gracefulDeadlineMs: number;
  restartHandoff: RestartHandoffDeps;
}

export async function runShutdownSequence(options: ShutdownSequenceOptions): Promise<void> {
  if (options.isShuttingDown()) {
    logger.warn('SYSTEM', 'Shutdown already in progress — ignoring re-entrant shutdown request', {
      reason: options.reason,
    });
    return;
  }
  options.markShuttingDown();

  try {
    await options.beforeGracefulShutdown();
  } catch (error: unknown) {
    // Pre-graceful bookkeeping (watcher/heartbeat/sentinel/telemetry flush)
    // failing must not abort the sequence: graceful shutdown and — for
    // restarts — the successor handoff still have to run. Same "proceed on
    // error, never abort the handoff" policy as performGracefulShutdown below.
    logger.error(
      'SYSTEM',
      'Pre-graceful shutdown bookkeeping failed — proceeding',
      { reason: options.reason },
      error instanceof Error ? error : new Error(String(error))

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Treat it as benign — the first shutdown continues; just wait for process exit
  2. If the process never exits afterwards, the first shutdown is hung — inspect the graceful-shutdown deadline warning and hanging resources
  3. Configure supervisors to send one signal and wait (e.g. docker StopSignal + generous stop timeout)
Defensive patterns

Strategy: validation

Validate before calling

async function shutdownOnce(worker: WorkerService, reason: string): Promise<void> {
  if (worker.isShuttingDown) return; // idempotent guard on the caller side too
  await worker.shutdown(reason);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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