From 664878b0baa0c99ce58a40a13e09140df6f9f261 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:43:31 +0530 Subject: [PATCH] fix: guard /api/status active count against missing state.db Review finding: SessionDB(read_only=True) requires the DB file to exist (its documented contract says callers guard on db_path.exists()); on a fresh install every /api/status poll paid an OperationalError until the first session was written. Short-circuit to 0 when state.db is absent. Tests: fresh-install guard + existing read_only test adjusted. --- hermes_cli/web_server.py | 8 +++++++- tests/hermes_cli/test_web_server.py | 23 ++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 5645f91ddf7..874accdb0a9 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -1183,7 +1183,13 @@ def _count_status_active_sessions() -> int: connection so /api/status never tries to initialise or migrate state.db while another Hermes process is writing to it. """ - from hermes_state import SessionDB + from hermes_state import DEFAULT_DB_PATH, SessionDB + + # read_only opens require the DB to already exist (see SessionDB.__init__ + # read_only contract) — on a fresh install every /api/status poll would + # otherwise pay an OperationalError until the first session is written. + if not DEFAULT_DB_PATH.exists(): + return 0 db = SessionDB(read_only=True) try: diff --git a/tests/hermes_cli/test_web_server.py b/tests/hermes_cli/test_web_server.py index c67371b4e33..87860b5d9ff 100644 --- a/tests/hermes_cli/test_web_server.py +++ b/tests/hermes_cli/test_web_server.py @@ -267,8 +267,15 @@ class TestWebServerEndpoints: assert "active_sessions" in data assert data["can_update_hermes"] is True - def test_status_active_session_count_uses_read_only_db(self, monkeypatch): + def test_status_active_session_count_uses_read_only_db(self, monkeypatch, tmp_path): import hermes_cli.web_server as web_server + import hermes_state + + # Satisfy the fresh-install guard: read_only opens require the DB + # file to already exist. + fake_db_path = tmp_path / "state.db" + fake_db_path.touch() + monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", fake_db_path) captured = {} @@ -293,6 +300,20 @@ class TestWebServerEndpoints: assert web_server._count_status_active_sessions() == 1 assert captured == {"read_only": True, "limit": 50, "closed": True} + def test_status_active_session_count_fresh_install_returns_zero(self, monkeypatch, tmp_path): + """No state.db yet (fresh install): return 0 without attempting a + read-only open, which would raise OperationalError on every poll.""" + import hermes_cli.web_server as web_server + import hermes_state + + monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", tmp_path / "absent.db") + + def _boom(*a, **k): + raise AssertionError("SessionDB must not be constructed when db file is absent") + + monkeypatch.setattr("hermes_state.SessionDB", _boom) + assert web_server._count_status_active_sessions() == 0 + def test_get_status_degrades_when_active_session_count_fails(self, monkeypatch): import hermes_cli.web_server as web_server