fix: close leaked SessionDB connections on exception paths (#83226) - #83237
JonthanaHanh wants to merge 1 commit into
Conversation
…rch#83226) Two call sites create SessionDB instances without closing them on error: 1. gateway/slash_commands.py: /insights command — db.close() was on the success path but not in a finally block, so exceptions between SessionDB() and db.close() leak the connection. 2. hermes_cli/sessions_cmd.py: sessions repair — SessionDB() created inline with no .close() at all, leaking the FD on every call. Additionally, add a __del__ safety net to SessionDB itself so that instances orphaned by callers who forget .close() are cleaned up when garbage collected, rather than pinning FDs alive until process exit via the atexit hook. Fixes NousResearch#83226
|
suggesting changes
The two explicit call-site changes do close correctly on their exception paths, but the new fallback cannot recover any other forgotten close after token accounting starts. Please either remove the ineffective finalizer and its claim, or break the atexit strong-retention cycle/establish equivalent deterministic ownership, with a regression test that starts token accounting, drops the caller reference, collects, and verifies that both the instance and its descriptors are gone. Security evidence:
Not checked:
Signed: GPT-5.6-sol-xhigh in Codex |
…air exception paths (#83226) Two call sites create SessionDB instances without closing them on error: 1. gateway/slash_commands.py: /insights command - db.close() was on the success path but not in a finally block, so exceptions between SessionDB() and db.close() leak the connection. 2. hermes_cli/sessions_cmd.py: sessions repair - SessionDB() created inline with no .close() at all, leaking the FD on every call. Salvage note: the original PR (#83237) also added a __del__ safety-net finalizer to SessionDB; review showed the atexit hook registered by queue_token_counts() strongly retains the instance, so the finalizer never fires for the leak class it claimed to cover. Dropped here in favor of the deterministic constructor-finally ownership repair salvaged from #83620.
|
Merged via PR #86667 — your commit (the One part was not carried: the |
|
Merged via #86691 — your commits cherry-picked with authorship preserved via rebase-merge. Your fix for the SessionDB FD leak is now on main. The two try/finally wrappers in Thanks for the clear issue report (#83226) and the well-targeted fix. |
…air exception paths (NousResearch#83226) Two call sites create SessionDB instances without closing them on error: 1. gateway/slash_commands.py: /insights command - db.close() was on the success path but not in a finally block, so exceptions between SessionDB() and db.close() leak the connection. 2. hermes_cli/sessions_cmd.py: sessions repair - SessionDB() created inline with no .close() at all, leaking the FD on every call. Salvage note: the original PR (NousResearch#83237) also added a __del__ safety-net finalizer to SessionDB; review showed the atexit hook registered by queue_token_counts() strongly retains the instance, so the finalizer never fires for the leak class it claimed to cover. Dropped here in favor of the deterministic constructor-finally ownership repair salvaged from NousResearch#83620.
…air exception paths (NousResearch#83226) Two call sites create SessionDB instances without closing them on error: 1. gateway/slash_commands.py: /insights command - db.close() was on the success path but not in a finally block, so exceptions between SessionDB() and db.close() leak the connection. 2. hermes_cli/sessions_cmd.py: sessions repair - SessionDB() created inline with no .close() at all, leaking the FD on every call. Salvage note: the original PR (NousResearch#83237) also added a __del__ safety-net finalizer to SessionDB; review showed the atexit hook registered by queue_token_counts() strongly retains the instance, so the finalizer never fires for the leak class it claimed to cover. Dropped here in favor of the deterministic constructor-finally ownership repair salvaged from NousResearch#83620.
Summary
Fixes leaked
SessionDBSQLite file descriptors on two exception paths that accumulate until the process hits EMFILE (Too many open files).Changes
gateway/slash_commands.py—/insightscommand:db.close()was on the success path but not wrapped intry/finally. IfInsightsEngine.generate()orformat_gateway()raises, the connection leaks.hermes_cli/sessions_cmd.py—sessions repair:SessionDB()was created inline with no.close()at all, leaking the FD on every invocation.hermes_state.py— Added__del__safety net toSessionDBso instances orphaned by callers who forget.close()are cleaned up when garbage collected, rather than pinning FDs alive until process exit via the atexit hook.Notes
The 4 other leak sites mentioned in the issue (
tui_gateway/server.py,tui_gateway/compute_host.py,tui_gateway/methods_session.py×2) already have properfinallyblocks with_transfer_db_to_agent/owns_dbguards on current main.Fixes #83226