Skip to content

fix(db): close SQLite FDs on context exit and fallback gracefully if FTS5 is missing - #67389

Closed
CryptoByz wants to merge 1 commit into
NousResearch:mainfrom
CryptoByz:fix/sqlite-fts-combo-fixes
Closed

fix(db): close SQLite FDs on context exit and fallback gracefully if FTS5 is missing#67389
CryptoByz wants to merge 1 commit into
NousResearch:mainfrom
CryptoByz:fix/sqlite-fts-combo-fixes

Conversation

@CryptoByz

Copy link
Copy Markdown
Contributor

Fixes #33580
Fixes #13029

What changed and why

Under long-lived daemon environments (such as the gateway dispatcher or web dashboard), processes eventually hit the kernel FD limit and crash with [Errno 24] Too many open files. This happens because kanban_db.connect() returned a plain sqlite3.Connection object, which has an __exit__ context manager method that only manages commits/rollbacks and fails to close the underlying file descriptor. Additionally, on specific platforms or custom Python builds lacking the SQLite fts5 module or trigram tokenizer (commonly Python 3.11 on macOS), session database initialization throws an unhandled sqlite3.OperationalError during migration or table creation, preventing the entire application from starting up instead of degrading search features gracefully.

In stage one of the root cause, using standard connection contexts like with kb.connect() as conn: leaks file descriptors to kanban.db and its WAL sidecar files upon exit. In stage two, the custom workaround connect_closing() was only applied to a subset of call sites while over 150 test suites and CLI routines continued to use the unsafe connect() context directly. For the FTS5 issue, the schema initialization and data migrations unconditionally executed CREATE VIRTUAL TABLE USING fts5 statements. Without catching and isolating these errors, a lack of the compiled extension causes immediate startup crashes rather than falling back to normal table structures and SQL LIKE-based queries.

Fix

hermes_cli/kanban_db.py

  • Introduced _ConnContext(sqlite3.Connection), a custom subclass overriding __exit__ to guarantee connection closure via self.close() on context exit regardless of transaction outcome.
  • Updated connect() to pass factory=_ConnContext to sqlite3.connect(), making all returned connections safe by default without altering existing with connect() call sites.

hermes_state.py

  • Added exception safety in SessionDB.__init__ by ensuring any error during initialization (e.g. database locking or setup failure) immediately closes self._conn before raising, avoiding unclosed connection leaks.
  • Wrapped FTS5 and trigram tokenizer setup statements in _init_schema inside try-except blocks using new static helpers _is_fts5_unavailable_error() and _is_trigram_unavailable_error().
  • Protected current_version < 10 and current_version < 11 migration blocks from crashing when executing virtual table recreation on systems without FTS5 or the trigram tokenizer, allowing normal tables and indices to be upgraded cleanly.

tests/hermes_cli/test_kanban_db.py

  • Added test_connect_closes_fd_on_context_manager_exit to verify file descriptor cleanup.
  • Added test_connect_context_manager_closes_on_exception to ensure exception paths close connections.
  • Added test_connect_returns_conn_context_type to assert the correct subclass is returned.
  • Updated wal_blocking_connect mock to dynamically subclass the passed factory class to prevent multiple values for keyword argument factory exceptions during testing.

What this does NOT change

This does not change the core CRUD SQL logic or database constraints. FTS5 full-text search behavior remains unchanged on environments where FTS5 is compiled and active. The connect_closing() utility is preserved for backwards compatibility.

How to test

Run the pytest suite to verify all existing and new tests pass:

.venv/bin/pytest tests/hermes_cli/test_kanban_db.py
.venv/bin/pytest tests/test_hermes_state.py

Platforms tested

Linux.

@alt-glitch alt-glitch added type/bug Something isn't working comp/cron Cron scheduler and job management comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jul 19, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to closed FD-repair predecessor #54218 and merged FTS5 work #35103. The current branch still has a useful _ConnContext repair, but its FTS5/trigram portion overlaps current main; please rebase and split the residual FD fix.

@CryptoByz

Copy link
Copy Markdown
Contributor Author

Closing as superseded.

While researching the root cause, I found that upstream already landed the same fix independently via multiple commits that are now in main:

  • b405c3980fix(kanban): close kanban SQLite FDs on context-manager exit (_ConnContext)
  • 5eb55835ffix: close kanban SQLite FDs on context-manager exit (_ConnContext)
  • 0773b6644 / 99e1d39c1 — same fix (additional contributor passes)

The _ConnContext subclass approach in those commits is identical to what this PR implemented. There is no remaining delta to merge, and rebasing onto current main produces a clean empty diff on all three changed files.

Thanks for the parallel work on this — glad the approach was validated by independent discovery.

@CryptoByz CryptoByz closed this Jul 19, 2026
@GottZ GottZ mentioned this pull request Jul 22, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cron Cron scheduler and job management needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

2 participants