← Back to PRs

#15882: fix: move session entry computation inside store lock to prevent race conditions

by cloorus open 2026-02-14 00:58 View on GitHub →
commands stale size: S
## Summary Fixes a race condition in `updateSessionStoreAfterAgentRun()` where the updated session entry was computed **outside** the write lock using a potentially-stale in-memory snapshot. ## Problem In multi-agent setups (multiple agents sharing one session store file), two concurrent agent runs could: 1. Both read the same in-memory `sessionStore` snapshot 2. Compute their updates independently 3. The last writer silently overwrites the other's changes This caused **session data contamination** across agents — users bound to different agents via Telegram DM bindings would receive each other's messages. ## Fix Moved the entry construction into the `updateSessionStore()` mutator callback, which: - Re-reads from disk **inside** the lock (via `loadSessionStore({ skipCache: true })`) - Guarantees each write merges against the latest persisted state - Keeps the caller's in-memory reference consistent ## Testing Reproduced in a 3-agent setup (main + 2 DM-bound agents via Telegram bindings). Before the fix, concurrent messages caused cross-agent session overwrites. After the fix, each agent's session state remains isolated. ## Related - Refs #12550 (Session Management and Data Integrity Defects) - Partially addresses concerns in #14411 (reducing blast radius of shared state) --- *First contribution from the CPIC (Colegio de Profesionales en Informática y Computación, Costa Rica) team. We run OpenClaw in production with multi-agent Telegram bindings and hit this issue directly.* <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR updates `updateSessionStoreAfterAgentRun()` to construct the next `SessionEntry` *inside* the `updateSessionStore()` mutator callback. Because `updateSessionStore()` re-reads the session store from disk with `skipCache: true` while holding the per-store write lock, building/merging the entry inside the callback prevents concurrent agents (sharing the same store file) from computing updates against a stale in-memory snapshot and then overwriting each other. Net effect: session store writes now merge against the latest persisted state under lock, and the caller’s in-memory `sessionStore[sessionKey]` is updated within the same critical section to keep the in-process snapshot consistent with what’s written to disk. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - Change is narrowly scoped to moving session-entry computation into the already-existing locked updateSessionStore mutator, aligning with updateSessionStore’s contract (fresh read inside lock, then mutate, then save). No behavioral changes beyond eliminating a lost-update race; no new I/O paths or error handling changes introduced. - No files require special attention <sub>Last reviewed commit: 3e72d9c</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs