Skip to content

Browser tabs not closed on session end — causes unbounded memory growth #36666

@Harnoor6693

Description

@Harnoor6693

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 browser in tools.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:

  1. Clearing session queues
  2. Clearing bootstrap snapshots
  3. Stopping subagents
  4. 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

  1. Added browser cleanup instructions to SOUL.md telling agents to close tabs after use
  2. 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
done

Neither 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions