← Back to PRs

#15628: fix: resolve session write lock race condition

by 1kuna open 2026-02-13 17:40 View on GitHub →
agents stale size: S
Fixes #15623 ## Problem In `src/agents/session-write-lock.ts`, the final `release()` path deleted the in-process `HELD_LOCKS` entry **before** async cleanup (`handle.close()` + `fs.rm(lockPath)`). During that async gap, concurrent acquires in the same process could see no `HELD_LOCKS` entry but still observe the lock file on disk and spin on the filesystem retry loop until the 10s timeout. ## Solution - Add `releasing?: Promise<void>` to the held lock entry. - On final release, set `held.releasing` **before** any `await` so new acquires can detect teardown-in-progress. - If an acquire observes `held.releasing`, it waits for the promise (bounded by the overall acquire timeout) rather than spinning on the filesystem lock file. - Wrap `handle.close()` in `catch` so lock file removal still runs even if close fails. ## Behavioral change Acquires that race with an in-process release now wait for the release to complete instead of polling the filesystem lock file, preventing intermittent 10s timeouts under high concurrency. <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR updates `src/agents/session-write-lock.ts` to prevent an in-process acquire/release race: the held-lock entry now tracks a `releasing` promise so that new acquires can wait for teardown-in-progress instead of falling back to filesystem polling on the `.lock` file. The implementation centralizes release logic in `releaseHeldLock()`, sets `held.releasing` before awaiting `handle.close()` / `fs.rm()`, and makes teardown best-effort by swallowing `close()` errors so lock-file removal still runs. This aligns with how the lock is used by embedded runner flows that acquire once and release in a `finally` block. I didn’t find any must-fix issues introduced by this change. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - Change is localized to session write-lock acquisition/release, addresses a clear race window, preserves existing semantics for nested acquires, and callers in the codebase release exactly once in finally blocks. No new behavior outside the lock coordination path was found. - src/agents/session-write-lock.ts <sub>Last reviewed commit: aa99290</sub> <!-- greptile_other_comments_section --> **Context used:** - Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8)) - Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13)) <!-- /greptile_comment -->

Most Similar PRs