From c45e2b19c3cc1f623a89f7409145223cf65fb238 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Sat, 22 Aug 2026 03:15:16 +0530 Subject: [PATCH] fix(state): guard gateway FTS rebuild + comment early flag-set Add the foreign-holder guard to gateway/session.py::_rebuild_fts_once(), the third FTS rebuild path that was not covered by the original fix. Also add a comment explaining why _fts_runtime_rebuild_attempted is set before the foreign-holder check: the fail-open path that follows persists FTS_STALE_KEY so the next startup retries via _recover_stale_fts. --- gateway/session.py | 13 +++++++++++++ hermes_state.py | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/gateway/session.py b/gateway/session.py index 3ba5b10c48..be74201c9f 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -3875,6 +3875,19 @@ class SessionStore: db = self._db if db is None or not hasattr(db, "rebuild_fts"): return False + # Guard against the same WAL split-brain risk as the automatic + # rebuild paths: skip when a foreign process holds state.db or + # its WAL sidecars open. + if hasattr(db, "_foreign_state_db_holders"): + foreign_holders = db._foreign_state_db_holders() + if foreign_holders: + logger.warning( + "Skipping Session DB FTS rebuild while foreign processes " + "hold the database or WAL sidecars (%s); canonical " + "transcript writes remain available.", + foreign_holders, + ) + return False try: rebuilt = db.rebuild_fts() except Exception as exc: diff --git a/hermes_state.py b/hermes_state.py index a7d706e296..c795815164 100644 --- a/hermes_state.py +++ b/hermes_state.py @@ -4286,6 +4286,12 @@ class SessionDB(SessionSearchMixin, SessionSchemaMixin, SessionPortabilityMixin) return False if not self._is_fts_write_corruption_error(exc): return False + # Set the one-shot flag before the foreign-holder check: even when + # the rebuild is skipped, the fail-open path that follows persists + # the FTS_STALE_KEY marker so the next process startup will retry + # via _recover_stale_fts (which has its own holder guard). Setting + # the flag here also avoids re-running the expensive psutil scan on + # every subsequent corrupted write through this instance. self._fts_runtime_rebuild_attempted = True foreign_holders = self._foreign_state_db_holders() if foreign_holders: