-
-
Notifications
You must be signed in to change notification settings - Fork 69.4k
Browser tabs not closed on session end — causes unbounded memory growth #36666
Description
Summary
When agents use the browser tool during a session, pages/tabs opened via createPageViaPlaywright are never closed when the session ends. Over time, this causes unbounded Chromium renderer process accumulation and significant memory bloat on the host.
Environment
- OpenClaw version: 2026.3.2
- OS: Ubuntu 24.04 (headless server)
- Browser: Chromium via Playwright (headless,
--no-sandbox) - Setup: 10 agents (Cortex router + 9 specialists), all with
browserintools.allow
Observed behavior
After ~2 days of normal agent usage:
- 52 Chrome processes (main + GPU + network + 13+ renderer processes)
- 8 orphaned page tabs left open (rogers.com, techcrunch.com, news.google.com, canada.ca, example.com)
- ~2.1 GB RAM consumed by Chrome alone on a 16 GB server
- 44 iframes and 4 service workers from ad trackers on those stale pages, continuing to consume resources
After manually closing the 7 non-blank tabs via CDP (/json/close/{targetId}), RAM dropped from 3.2 GB to 2.4 GB and Chrome processes dropped from 52 to 19.
Root cause
ensureSessionRuntimeCleanup in gateway-cli-vk3t7zJU.js (line ~8851) handles session teardown by:
- Clearing session queues
- Clearing bootstrap snapshots
- Stopping subagents
- Aborting embedded PI runs
However, it does not close any browser tabs/pages that were opened during that session. There is also no configuration option (e.g., browser.autoCloseOnSessionEnd) to enable this behavior.
The closePageViaPlaywright and closePageByTargetIdViaPlaywright functions exist in pw-ai-gGTz8ayf.js and work correctly — they're just never called during session cleanup.
Expected behavior
When an agent session ends (whether normally or via cleanup/expiry), all browser tabs opened during that session should be closed automatically. This should happen in ensureSessionRuntimeCleanup or an equivalent lifecycle hook.
Suggested fix
Option A (preferred): Track which targetIds were opened during each session (the gateway already has session context) and close them in ensureSessionRuntimeCleanup via closePageByTargetIdViaPlaywright.
Option B: Add a browser.maxPages or browser.autoCloseIdleTabs configuration option that limits concurrent pages or closes tabs that have been idle for a configurable duration.
Option C: At minimum, provide a lifecycle hook (e.g., onSessionEnd) that users can configure to run cleanup commands, similar to how compaction.memoryFlush works for memory.
Current workaround
- Added browser cleanup instructions to
SOUL.mdtelling agents to close tabs after use - Created a cron job running every 30 minutes that closes all non-blank tabs via CDP:
#!/bin/bash
CDP_URL="http://localhost:18800"
pages=$(curl -s "$CDP_URL/json/list" 2>/dev/null)
[ -z "$pages" ] || [ "$pages" = "[]" ] && exit 0
targets=$(echo "$pages" | python3 -c "
import sys, json
pages = json.load(sys.stdin)
for p in pages:
if p.get('type') == 'page' and p.get('url', '') != 'about:blank':
print(p['id'])
" 2>/dev/null)
[ -z "$targets" ] && exit 0
for tid in $targets; do
curl -s "$CDP_URL/json/close/$tid" > /dev/null 2>&1
doneNeither workaround is reliable — LLM-based agents don't always follow cleanup instructions, and the cron approach can close tabs that are actively in use by a running session.
Impact
On long-running servers with multiple agents using the browser tool, this is a memory leak that will eventually cause OOM or severe performance degradation. The longer the server runs without a restart, the worse it gets.