thedotmack/claude-mem · warning
Graceful shutdown deadline exceeded — proceeding
Error message
Graceful shutdown deadline exceeded — proceeding
What it means
runShutdownSequence races graceful cleanup against a deadline. When the graceful branch loses the race, the sequence logs that the deadline was exceeded and proceeds (exits) anyway rather than hanging forever. In-flight work may be truncated.
Solutions
- Increase gracefulDeadlineMs in shutdown options to cover worst-case flush time
- Check what the graceful branch was doing (queue depth, SDK logs) when it timed out
- Reduce pending work at shutdown time (flush more frequently during operation)
- Ensure the SDK subprocess is killed promptly so it cannot block the race
Example fix
// before
runShutdownSequence({ gracefulDeadlineMs: 3000, ... })
// after
runShutdownSequence({ gracefulDeadlineMs: 10000, ... }) Defensive patterns
Strategy: try-catch
Validate before calling
// Estimate flush time before entering shutdown const pending = getPendingObservationCount(); const safeDeadline = Math.max(gracefulDeadlineMs, pending * PER_ITEM_MS + 2000);
Try / catch
try {
await runShutdownSequence({ gracefulDeadlineMs: 10000, ... });
} finally {
// exit anyway; deadline already enforced internally
} Prevention
- Set gracefulDeadlineMs generously relative to flush volume
- Flush observations incrementally so shutdown has little pending work
- Monitor deadline-exceeded warnings to tune the timeout
When it happens
Trigger: The graceful shutdown work (flushing observations, SDK queries, HTTP shutdown POST) did not complete before options.gracefulDeadlineMs elapsed, so Promise.race resolved with 'deadline' and the warn fires.
Common situations: Slow disk or DB making observation flush exceed the deadline; an SDK query hanging during shutdown; deadline configured too aggressively for the machine; large queue of pending memory writes at exit.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Worker service did not confirm shutdown; continuing…
- Live PID detected but worker did not become ready before…
- Request timed out after
- Self-replacing worker handoff did not verify in time —…
- Shutdown already in progress — ignoring re-entrant shutdown…
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/64322dd5d4dc1da6.
Report an issue: GitHub.
Appendix: 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 d8bc9755e7)