thedotmack/claude-mem · warning

Graceful shutdown deadline exceeded — proceeding

Error message

Graceful shutdown deadline exceeded — proceeding

What it means

Graceful shutdown (closing the HTTP server, flushing state) races a deadline timer via Promise.race. When the deadline (gracefulDeadlineMs) expires first, this warning fires and shutdown proceeds to the next phase anyway — the process still exits, just without confirming graceful cleanup finished.

Source

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

    const outcome = await Promise.race([
      options.performGracefulShutdown().then(
        () => 'graceful' as const,
        (error: unknown) => {
          // A failed graceful shutdown must not abort the restart handoff;
          // proceed exactly like the deadline path.
          logger.error(
            'SYSTEM',
            'Graceful shutdown failed — proceeding',
            { reason: options.reason },
            error instanceof Error ? error : new Error(String(error))
          );
          return 'graceful-error' as const;
        }
      ),
      deadline,
    ]);
    if (outcome === 'deadline') {
      logger.warn('SYSTEM', 'Graceful shutdown deadline exceeded — proceeding', {
        deadlineMs: options.gracefulDeadlineMs,
        reason: options.reason,
      });
    }
  } finally {
    if (deadlineTimer !== undefined) clearTimeout(deadlineTimer);
  }

  // Successor handoff — ONLY for restart; 'stop' and signal shutdowns stay
  // kill-only. The old worker spawns its replacement as its final act, after
  // its port is confirmed free, so the successor never races the corpse for
  // the port. CLI `claude-mem restart` is the caller. Hook version-mismatch
  // recycles (ensureWorkerRunning in src/shared/worker-utils.ts) never reach
  // this: they SIGKILL the stale worker and lazy-spawn the resolved version
  // themselves, because this handoff runs the DYING install's resolver — a
  // stale install would respawn its own version forever (#3378). This runs
  // inside flushResponseThen's flushed action, so it completes before that
  // helper's process.exit(0).

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Usually safe to ignore — shutdown continues and the process exits; verify the next `start` is clean
  2. If state (e.g. last observations) matters, find the hanging resource: `lsof -p <workerPid>` shows open sockets/files during shutdown
  3. Raise gracefulDeadlineMs in settings if your flushes legitimately need longer
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: Keep-alive HTTP connections refusing to close; a telemetry or memory flush blocked on network; an in-flight SDK query whose abort is delayed; filesystem stall on the DB directory.

Common situations: Long-running hook clients holding sockets open; proxied or containerized environments where connection close semantics differ; slow disks stretching the flush step past the deadline.

Understand the failure class

Related errors


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