unclecode/crawl4ai · warning · HTTPException

Cannot kill permanent browser. Use restart instead.

Error message

Cannot kill permanent browser. Use restart instead.

What it means

An explicit 403 raised by POST /monitor/actions/kill_browser when the requested signature prefix matches DEFAULT_CONFIG_SIG - the permanent browser. The permanent browser is the always-on instance serving default-config requests, so killing it would degrade every default crawl; the API intentionally refuses and directs you to restart_browser instead.

Source

Thrown at deploy/docker/monitor_routes.py:221

        async with LOCK:
            # Check hot pool
            for sig in HOT_POOL.keys():
                if sig.startswith(req.sig):
                    target_sig = sig
                    pool_type = "hot"
                    break

            # Check cold pool
            if not target_sig:
                for sig in COLD_POOL.keys():
                    if sig.startswith(req.sig):
                        target_sig = sig
                        pool_type = "cold"
                        break

            # Check if trying to kill permanent
            if DEFAULT_CONFIG_SIG and DEFAULT_CONFIG_SIG.startswith(req.sig):
                raise HTTPException(403, "Cannot kill permanent browser. Use restart instead.")

            if not target_sig:
                raise HTTPException(404, f"Browser with sig={req.sig} not found")

            # Warn if there are active requests (browser might be in use)
            monitor = get_monitor()
            active_count = len(monitor.get_active_requests())
            if active_count > 0:
                logger.warning(f"Killing browser {target_sig[:8]} while {active_count} requests are active - may cause failures")

            # Kill the browser
            if pool_type == "hot":
                browser = HOT_POOL.pop(target_sig)
            else:
                browser = COLD_POOL.pop(target_sig)

            with suppress(Exception):
                await browser.close()

View on GitHub (pinned to 7e80152142)

Solutions

  1. Use POST /monitor/actions/restart_browser instead - it explicitly supports the permanent browser (sig or 'permanent') via kill + recreate.
  2. Confirm the target's pool type from GET /monitor/browsers before killing; permanent is neither hot nor cold.
  3. Adjust automation to exclude the default signature when selecting kill candidates.

Example fix

# before
post(f"{base}/monitor/actions/kill_browser", json={"sig": sig})  # 403 if permanent

# after
if sig == 'permanent' or sig == default_sig[:8]:
    post(f"{base}/monitor/actions/restart_browser", json={"sig": sig})
else:
    post(f"{base}/monitor/actions/kill_browser", json={"sig": sig})
Defensive patterns

Strategy: type-guard

Validate before calling

def is_permanent_sig(sig: str, default_sig: str) -> bool:
    return bool(default_sig) and default_sig.startswith(sig)

# route to restart instead of kill when target is permanent
action = 'restart_browser' if is_permanent_sig(sig, default_sig) else 'kill_browser'

Type guard

def can_kill(sig: str, default_sig: str | None) -> bool:
    """True when sig does NOT resolve to the permanent default browser."""
    return not (default_sig and default_sig.startswith(sig))

Try / catch

try:
    resp = post(f"{base}/monitor/actions/kill_browser", json={'sig': sig})
except HTTPError as e:
    if e.response.status_code == 403:
        resp = post(f"{base}/monitor/actions/restart_browser", json={'sig': sig})
    else:
        raise

Prevention

When it happens

Trigger: Calling POST /monitor/actions/kill_browser with sig equal to (or a prefix of) the default config signature - e.g. sig='permanent' if that prefix matches, or the first 8 chars of DEFAULT_CONFIG_SIG obtained from /monitor/browsers.

Common situations: Operator sees a bloated/leaky browser in the dashboard, copies its sig, and does not notice it is the permanent one; automation written to 'recycle any browser over N MB' hits the permanent instance; attempts to free memory by killing the largest browser, which is usually permanent.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/94807ea7f62f3ec6. Report an issue: GitHub.