From ec0728b3574f115c83fd458967ea9493f388c647 Mon Sep 17 00:00:00 2001 From: Sash Zats Date: Thu, 5 Feb 2026 19:35:34 -0500 Subject: [PATCH] fix: release session locks on process termination (#1962) Adds cleanup handlers to release held file locks when the process terminates via SIGTERM, SIGINT, or normal exit. This prevents orphaned lock files that would block future sessions. Fixes #1951 --- src/agents/session-write-lock.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/agents/session-write-lock.ts b/src/agents/session-write-lock.ts index 7335abaf0..73afe6dc9 100644 --- a/src/agents/session-write-lock.ts +++ b/src/agents/session-write-lock.ts @@ -18,6 +18,32 @@ const CLEANUP_SIGNALS = ["SIGINT", "SIGTERM", "SIGQUIT", "SIGABRT"] as const; type CleanupSignal = (typeof CLEANUP_SIGNALS)[number]; const cleanupHandlers = new Map void>(); +/** + * Release all held locks - called on process exit to prevent orphaned locks + */ +async function releaseAllLocks(): Promise { + const locks = Array.from(HELD_LOCKS.values()); + HELD_LOCKS.clear(); + for (const lock of locks) { + try { + await lock.handle.close(); + await fs.rm(lock.lockPath, { force: true }); + } catch { + // Best effort cleanup + } + } +} + +// Register cleanup handlers to release locks on unexpected termination +process.on("exit", releaseAllLocks); +process.on("SIGTERM", () => { + void releaseAllLocks().then(() => process.exit(0)); +}); +process.on("SIGINT", () => { + void releaseAllLocks().then(() => process.exit(0)); +}); +// Note: unhandledRejection handler will call process.exit() which triggers 'exit' + function isAlive(pid: number): boolean { if (!Number.isFinite(pid) || pid <= 0) { return false;