fix: close leaked state.db read-only connections in session discoverability/recovery

Three sites used `with sqlite3.connect(f"file:...?mode=ro", uri=True) as conn:`,
which only scopes the transaction — sqlite3.Connection's context manager never
calls close(). Same recurring bug class as #1421/#1494/#2233/#3904. On a
long-lived server each poll leaked a state.db (+ -wal/-shm) fd until the
process hit macOS's 256 soft FD limit and writes started failing.

Wrap all three in contextlib.closing(), matching the pattern already used
elsewhere in these files.
This commit is contained in:
Randy Marsden authored and nesquena-hermes committed 2026-08-07 21:34:34 +00:00
1 parent 5f3117f81e
commit dcf881f1e8
2 files changed
+5 -3

No files matched your search

+2 -1
View File
@@ -17,6 +17,7 @@ import threading
import shutil
import sqlite3
from collections import Counter
from contextlib import closing
from pathlib import Path
from typing import Iterable
@@ -102,7 +103,7 @@ def _read_state_db(state_db_path: Path | None) -> dict[str, dict]:
if state_db_path is None or not state_db_path.exists():
return {}
try:
with sqlite3.connect(f"file:{state_db_path}?mode=ro", uri=True) as conn:
with closing(sqlite3.connect(f"file:{state_db_path}?mode=ro", uri=True)) as conn:
conn.row_factory = sqlite3.Row
tables = {row[0] for row in conn.execute("select name from sqlite_master where type='table'")}
if "sessions" not in tables:
+3 -2
View File
@@ -32,6 +32,7 @@ import os
import shutil
import sqlite3
import threading
from contextlib import closing
from pathlib import Path
from api.turn_journal import (
@@ -381,7 +382,7 @@ def _state_db_has_session(session_id: str, state_db_path: Path | None) -> bool:
if state_db_path is None or not state_db_path.exists():
return True
try:
with sqlite3.connect(f"file:{state_db_path}?mode=ro", uri=True) as conn:
with closing(sqlite3.connect(f"file:{state_db_path}?mode=ro", uri=True)) as conn:
cur = conn.execute(
"select 1 from sqlite_master where type='table' and name='sessions'"
)
@@ -446,7 +447,7 @@ def _read_state_db_missing_sidecar_rows(
if state_db_path is None or not state_db_path.exists():
return []
try:
with sqlite3.connect(f"file:{state_db_path}?mode=ro", uri=True) as conn:
with closing(sqlite3.connect(f"file:{state_db_path}?mode=ro", uri=True)) as conn:
conn.row_factory = sqlite3.Row
session_cols = {row[1] for row in conn.execute("PRAGMA table_info(sessions)").fetchall()}
message_cols = {row[1] for row in conn.execute("PRAGMA table_info(messages)").fetchall()}