Skip to content

fix: reap orphaned browser sessions on startup - #7931

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-168d98b8
Apr 11, 2026
Merged

fix: reap orphaned browser sessions on startup#7931
teknium1 merged 1 commit into
mainfrom
hermes/hermes-168d98b8

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

When the Python process that created a browser session exits uncleanly (SIGKILL, crash, gateway restart via hermes update), the in-memory _active_sessions tracking is lost but the agent-browser node daemons and their Chromium child processes keep running indefinitely.

Impact found on production: 24 orphaned sessions accumulated over 9 days, spawning 140 processes (node + Chromium trees) consuming 7.6 GB of RAM. Memory watchdog showed steady growth from 30% to 64% over 3 weeks, with these zombies as the primary cause.

Changes

  • Add _reap_orphaned_browser_sessions() to tools/browser_tool.py
    • Scans /tmp/agent-browser-{h_*,cdp_*}/ socket dirs on cleanup thread startup
    • For each dir not tracked by _active_sessions, reads the daemon PID file
    • SIGTERMs alive daemons, cleans up stale dirs
    • Handles: dead PIDs, corrupt PID files, PermissionError, foreign processes
    • Runs once on thread startup (not every 30s) to avoid races
  • Called at the beginning of _browser_cleanup_thread_worker() before the periodic loop
  • 9 new tests covering all edge cases

Test plan

python -m pytest tests/tools/test_browser_orphan_reaper.py -q  # 9 passed
python -m pytest tests/tools/test_browser_cleanup.py -q         # 6 passed  
# All 157 browser tests pass (1 pre-existing config_version failure unrelated)

When a Python process exits uncleanly (SIGKILL, crash, gateway restart
via hermes update), in-memory _active_sessions tracking is lost but the
agent-browser node daemons and their Chromium child processes keep
running indefinitely. On a long-running system this causes unbounded
memory growth — 24 orphaned sessions consumed 7.6 GB on a production
machine over 9 days.

Add _reap_orphaned_browser_sessions() which scans the tmp directory for
agent-browser-{h_*,cdp_*} socket dirs on cleanup thread startup.  For
each dir not tracked by the current process, reads the daemon PID file
and sends SIGTERM if the daemon is still alive.  Handles edge cases:
dead PIDs, corrupt PID files, permission errors, foreign processes.

The reaper runs once on thread startup (not every 30s) to avoid races
with sessions being actively created by concurrent agents.
@teknium1
teknium1 merged commit 75380de into main Apr 11, 2026
3 of 4 checks passed
@teknium1
teknium1 deleted the hermes/hermes-168d98b8 branch April 11, 2026 21:02
Tommyeds pushed a commit to Tommyeds/hermes-agent that referenced this pull request Apr 12, 2026
When a Python process exits uncleanly (SIGKILL, crash, gateway restart
via hermes update), in-memory _active_sessions tracking is lost but the
agent-browser node daemons and their Chromium child processes keep
running indefinitely. On a long-running system this causes unbounded
memory growth — 24 orphaned sessions consumed 7.6 GB on a production
machine over 9 days.

Add _reap_orphaned_browser_sessions() which scans the tmp directory for
agent-browser-{h_*,cdp_*} socket dirs on cleanup thread startup.  For
each dir not tracked by the current process, reads the daemon PID file
and sends SIGTERM if the daemon is still alive.  Handles edge cases:
dead PIDs, corrupt PID files, permission errors, foreign processes.

The reaper runs once on thread startup (not every 30s) to avoid races
with sessions being actively created by concurrent agents.
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
When a Python process exits uncleanly (SIGKILL, crash, gateway restart
via hermes update), in-memory _active_sessions tracking is lost but the
agent-browser node daemons and their Chromium child processes keep
running indefinitely. On a long-running system this causes unbounded
memory growth — 24 orphaned sessions consumed 7.6 GB on a production
machine over 9 days.

Add _reap_orphaned_browser_sessions() which scans the tmp directory for
agent-browser-{h_*,cdp_*} socket dirs on cleanup thread startup.  For
each dir not tracked by the current process, reads the daemon PID file
and sends SIGTERM if the daemon is still alive.  Handles edge cases:
dead PIDs, corrupt PID files, permission errors, foreign processes.

The reaper runs once on thread startup (not every 30s) to avoid races
with sessions being actively created by concurrent agents.
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
When a Python process exits uncleanly (SIGKILL, crash, gateway restart
via hermes update), in-memory _active_sessions tracking is lost but the
agent-browser node daemons and their Chromium child processes keep
running indefinitely. On a long-running system this causes unbounded
memory growth — 24 orphaned sessions consumed 7.6 GB on a production
machine over 9 days.

Add _reap_orphaned_browser_sessions() which scans the tmp directory for
agent-browser-{h_*,cdp_*} socket dirs on cleanup thread startup.  For
each dir not tracked by the current process, reads the daemon PID file
and sends SIGTERM if the daemon is still alive.  Handles edge cases:
dead PIDs, corrupt PID files, permission errors, foreign processes.

The reaper runs once on thread startup (not every 30s) to avoid races
with sessions being actively created by concurrent agents.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
When a Python process exits uncleanly (SIGKILL, crash, gateway restart
via hermes update), in-memory _active_sessions tracking is lost but the
agent-browser node daemons and their Chromium child processes keep
running indefinitely. On a long-running system this causes unbounded
memory growth — 24 orphaned sessions consumed 7.6 GB on a production
machine over 9 days.

Add _reap_orphaned_browser_sessions() which scans the tmp directory for
agent-browser-{h_*,cdp_*} socket dirs on cleanup thread startup.  For
each dir not tracked by the current process, reads the daemon PID file
and sends SIGTERM if the daemon is still alive.  Handles edge cases:
dead PIDs, corrupt PID files, permission errors, foreign processes.

The reaper runs once on thread startup (not every 30s) to avoid races
with sessions being actively created by concurrent agents.
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
When a Python process exits uncleanly (SIGKILL, crash, gateway restart
via hermes update), in-memory _active_sessions tracking is lost but the
agent-browser node daemons and their Chromium child processes keep
running indefinitely. On a long-running system this causes unbounded
memory growth — 24 orphaned sessions consumed 7.6 GB on a production
machine over 9 days.

Add _reap_orphaned_browser_sessions() which scans the tmp directory for
agent-browser-{h_*,cdp_*} socket dirs on cleanup thread startup.  For
each dir not tracked by the current process, reads the daemon PID file
and sends SIGTERM if the daemon is still alive.  Handles edge cases:
dead PIDs, corrupt PID files, permission errors, foreign processes.

The reaper runs once on thread startup (not every 30s) to avoid races
with sessions being actively created by concurrent agents.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant