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
- Usually safe to ignore — shutdown continues and the process exits; verify the next `start` is clean
- If state (e.g. last observations) matters, find the hanging resource: `lsof -p <workerPid>` shows open sockets/files during shutdown
- Raise gracefulDeadlineMs in settings if your flushes legitimately need longer
Defensive patterns
Strategy: fallback
Prevention
- Make shutdown-phase work idempotent (flushes, checkpoints) so proceeding past the deadline is safe
- Close long-lived client connections to the worker before triggering shutdown
- Size gracefulDeadlineMs to your worst realistic flush, not the default
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- [uninstall] Worker shutdown attempt failed:
- Unknown Claude model: ${options.model}. Allowed: ${[...allow
- Failed to install Bun. Please install manually: ${manualInst
- Shutdown request returned error
- Port did not free up after shutdown
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/64322dd5d4dc1da6.
Report an issue: GitHub.