Skip to content

fix(daemon): stop the remote browser on SIGTERM#475

Open
yangshu2087 wants to merge 1 commit into
browser-use:mainfrom
yangshu2087:fix/daemon-sigterm-graceful-stop
Open

fix(daemon): stop the remote browser on SIGTERM#475
yangshu2087 wants to merge 1 commit into
browser-use:mainfrom
yangshu2087:fix/daemon-sigterm-graceful-stop

Conversation

@yangshu2087

@yangshu2087 yangshu2087 commented Jun 27, 2026

Copy link
Copy Markdown

Problem

The daemon only handled SIGINT (KeyboardInterrupt). On SIGTERM the process was terminated before the finally block ran, so stop_remote() never fired and the remote browser leaked (and kept billing).

This path is hit in practice:

  • admin.py escalates to os.kill(pid, signal.SIGTERM) when the graceful "shutdown" IPC can't be delivered (hung/unreachable daemon).
  • Container runtimes (Docker/k8s) send SIGTERM on stop.

Fix

Register an asyncio SIGTERM handler that sets the existing stop Event — the same graceful path as the "shutdown" command — so main() unwinds cleanly and the finally block runs stop_remote().

  • Registered before start(), because get_ws_url() / the CDP handshake can take seconds and a signal during that window must still trigger cleanup. The stop Event is moved to __init__ so it exists in time.
  • POSIX-only: add_signal_handler raises NotImplementedError on Windows, where SIGTERM can't be caught anyway — guarded.

Test

Adds test_main_registers_sigterm_handler_that_triggers_graceful_stop: asserts main() registers the handler (via the public loop.remove_signal_handler, which returns True iff one was set) and that setting the stop Event drives a graceful return. No real signal is delivered (a regression would otherwise kill the test runner). Skipped on Windows.

Full suite: 115 passed.

🤖 Generated with Claude Code


Summary by cubic

Ensure the daemon stops the remote browser on SIGTERM to avoid leaks and surprise billing. A SIGTERM now triggers the same graceful shutdown path so stop_remote() always runs.

  • Bug Fixes
    • Add asyncio SIGTERM handler (POSIX-only) that sets the stop event; register it before start().
    • Create the stop event in init so signals during startup are honored.
    • Add a unit test that verifies the handler is registered and main() exits gracefully (skipped on Windows).

Written for commit cfd7e18. Summary will update on new commits.

Review in cubic

The daemon only caught SIGINT (KeyboardInterrupt); SIGTERM terminated the
process before the finally block, so stop_remote() never ran and the remote
browser leaked (and kept billing). admin.py escalates to os.kill(pid, SIGTERM)
when the "shutdown" IPC can't be delivered, and container runtimes send SIGTERM
on stop, so this path is hit in practice.

Register an asyncio SIGTERM handler that sets the existing stop Event — the
same graceful path as the "shutdown" command — so main() unwinds cleanly and
the finally block runs stop_remote(). Registered before start() (the CDP
handshake can take seconds) with the stop Event moved to __init__. POSIX-only:
add_signal_handler raises NotImplementedError on Windows, where SIGTERM can't
be caught anyway.

Adds a unit test asserting main() registers the handler and that the stop
Event drives a graceful return.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Re-trigger cubic

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