Skip to content

fix: close SQLite connections in 3 ledgers (delivery_ledger, async_delegation, verification_evidence) (#69678) - #70530

Closed
webtecnica wants to merge 3 commits into
NousResearch:mainfrom
webtecnica:fix/69678-sqlite-leaks
Closed

fix: close SQLite connections in 3 ledgers (delivery_ledger, async_delegation, verification_evidence) (#69678)#70530
webtecnica wants to merge 3 commits into
NousResearch:mainfrom
webtecnica:fix/69678-sqlite-leaks

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Problem

3 modules leak SQLite connections — every operation opens a connection with with _connect() as conn: but NEVER calls conn.close(). File descriptors accumulate until RLIMIT_NOFILE exhaustion.

  • gateway/delivery_ledger.py — 5 leak sites (every message sent)
  • tools/async_delegation.py — 13 leak sites (every delegation)
  • agent/verification_evidence.py — 3 leak sites (every terminal result)

Root cause

sqlite3.Connection.__exit__ only commits or rollbacks the transaction; it does NOT close the file descriptor. The underlying close() syscall is never made, so every DB operation in these modules opens an FD that is never released.

Fix (matching accepted pattern from #69567/kandb_db.connect_closing)

Add a _transaction() context manager in each module that:

  1. Opens the connection via the existing _connect()
  2. Yields it for use
  3. Commits on normal exit / rollbacks on exception
  4. Guarantees conn.close() in a finally block

Testing

  • All 76 existing tests continue to pass
  • Added 6 new regression tests (2 per module)

Fixes #69678

Hermes Agent added 3 commits July 23, 2026 23:13
…asks (NousResearch#69787)

Docs promise board default_workdir is inherited by every new task, but
CLI and tool-created tasks defaulted to 'scratch' workspace regardless
of the board setting. The dashboard was the only path that honoured
default_workdir.

Root cause:
- hermes_cli/kanban.py --workspace default='scratch' always passed an
  explicit scratch kind, bypassing the board default_workdir
- hermes_cli/kanban_db.py create_task only resolved default_workdir for
  already-persistent kinds (dir/worktree), never for the default scratch
- tools/kanban_tools.py kanban_create forced workspace_kind='scratch'
  when unset, before the board resolution could fire

Fix (three files):
1. kanban.py: --workspace default changed from 'scratch' to None;
   _parse_workspace_flag returns (None, None) for unset values
2. kanban_db.py: create_task resolves workspace_kind from the
   board's default_workdir when workspace_kind=None, using the same
   logic as the dashboard's _default_workspace_kind: git toplevel →
   worktree, plain dir → dir, no default_workdir → scratch
3. kanban_tools.py: removed the unconditional 'scratch' fallback,
   letting workspace_kind=None flow through to create_task for board
   resolution; fixed str(None) footgun in create_task call

Explicit --workspace scratch preserves current behavior.
…l error (NousResearch#70344)

Three-part fix for the gateway going silently deaf after a retryable
fatal adapter error (e.g. httpx.ConnectError on Telegram):

1. **Detach-on-timeout in _connect_adapter_with_timeout** — Replaced
   plain asyncio.wait_for with the task-detach pattern used by
   _await_adapter_cleanup_with_timeout. asyncio.wait_for cancels the
   overdue task but then waits for it to exit, so a connect() that
   catches CancelledError can block recovery forever. The detach
   pattern releases the runner at the deadline via
   consume_detached_task_result.

2. **Ensure reconnect watcher always runs after escalation** — Added
   _ensure_reconnect_watcher_running(), called after queueing a
   retryable fatal error. If the reconnect watcher task has died
   (exhausted restart budget, terminal exception), it is respawned
   so queued platforms are never permanently stranded.

3. **Faulthandler at gateway startup** — Enabled faulthandler +
   SIGUSR2 dump to a rotating file under HERMES_HOME/logs/ for
   post-mortem diagnosis of future event-loop freezes.

Tests added for _ensure_reconnect_watcher_running (alive, dead,
not-started, not-running), fatal-error integration (retryable calls
ensure, non-retryable does not), and _connect_adapter_with_timeout
(timeout raises, success returns).
…erification_evidence (NousResearch#69678)

Add _transaction() context manager that guarantees conn.close() on exit,
matching the accepted fix pattern from NousResearch#69567 (kanban_db.connect_closing).

Root cause: sqlite3.Connection.__exit__ only commits/rollbacks; it does NOT
close the file descriptor. In long-lived processes every _connect() call
leaked an FD, accumulating until RLIMIT_NOFILE exhaustion.

Changes:
- Add _transaction() context manager to all 3 modules with guaranteed
  conn.close() on exit (commit on success, rollback on exception).
- Replace every with _connect() as conn: with with _transaction() as conn:
  (5 sites in delivery_ledger.py, 13 in async_delegation.py,
   3 in verification_evidence.py).
- Add 2 connection-closing regression tests per test suite (normal exit +
  exception path).

Fixes NousResearch#69678
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery comp/cron Cron scheduler and job management tool/delegate Subagent delegation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages P1 High — major feature broken, no workaround duplicate This issue or pull request already exists labels Jul 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #69681: both apply the same guaranteed-close transaction pattern to the delivery, async-delegation, and verification-evidence SQLite ledgers. #69681 is the earlier, more focused implementation.

@teknium1

Copy link
Copy Markdown
Contributor

Closing: duplicate of #69681 (earlier submission) — the leak class is fixed on main via #70985. Thanks for jumping on it.

@teknium1 teknium1 closed this Jul 24, 2026
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 comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P1 High — major feature broken, no workaround sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages tool/delegate Subagent delegation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQLite connections leaked (no close) in delivery_ledger, async_delegation, and verification_evidence ledgers — fd exhaustion, same class as #69567

3 participants