Skip to content

fix: suppress CDP error -32000 to prevent session corruption#22

Open
FaisalFehad wants to merge 2 commits into
browser-use:mainfrom
FaisalFehad:fix/browser-window-not-found
Open

fix: suppress CDP error -32000 to prevent session corruption#22
FaisalFehad wants to merge 2 commits into
browser-use:mainfrom
FaisalFehad:fix/browser-window-not-found

Conversation

@FaisalFehad

@FaisalFehad FaisalFehad commented Jun 1, 2026

Copy link
Copy Markdown

Problem

When using browser automation tools that depend on cdp-use, file uploads and page transitions consistently cause browser sessions to become unusable with the error:

RuntimeError: {"code": -32000, "message": "Browser window not found"}

This makes all subsequent commands (state, wait stable, screenshot, etc.) fail, rendering the browser session permanently broken.

Root Cause

The error occurs during a race condition in the CDP event handler:

  1. _on_target_info_changed fires when a new target is discovered (e.g., file upload or page navigation creates a new page context)
  2. _trigger_page_target_discovered calls Browser.getWindowForTarget for the new target
  3. The target exists in CDP but doesn't yet have an associated window binding
  4. CDP returns error -32000 ("Browser window not found")
  5. The RuntimeError is raised and propagates through the event handler
  6. The unhandled exception crashes the event handler and corrupts the session
  7. All subsequent CDP calls fail because the session state is inconsistent

Fix

In CDPClient._handle_messages(), when a CDP response contains error -32000, return a default window result instead of raising RuntimeError. This allows the session to continue normally — the target will get its window binding shortly after.

Other CDP errors are still raised as RuntimeError as before.

Testing

Verified with browser-act:

  • ✅ Open browser session
  • ✅ Navigate to pages
  • ✅ File uploads (previously crashed the session)
  • ✅ Click buttons and interact with elements
  • state command works after interactions
  • wait stable command works after interactions
  • ✅ Complete automation flow works end-to-end

Changes

  • cdp_use/client.py: Added error suppression for CDP error code -32000 in _handle_messages(), returning a default window result {"windowId": 0, "bounds": {"left": 0, "top": 0, "width": 1920, "height": 1080, "windowState": "normal"}} instead of raising RuntimeError

When Browser.getWindowForTarget returns error -32000 ('Browser window not found'),
the CDP client raises RuntimeError which crashes the event handler and corrupts
the browser session. This occurs during race conditions when a target exists in
CDP but doesn't yet have an associated window (e.g., during file uploads, page
transitions, or new tab creation).

The fix returns a default window result instead of raising an exception, allowing
the session to continue normally. Other CDP errors are still raised as before.

Root cause analysis:
- _on_target_info_changed fires when a new target is discovered
- _trigger_page_target_discovered calls getWindowForTarget for the new target
- The target doesn't have a window yet, so CDP returns -32000
- The unhandled RuntimeError propagates up and corrupts the session
- All subsequent commands fail with 'Browser window not found'

Verified: file uploads, page navigation, and wait stable all work after this fix.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread cdp_use/client.py Outdated
Track the CDP method name alongside each pending request future so that
error -32000 suppression only applies to Browser.getWindowForTarget.
Previously, any CDP method returning -32000 would receive a fabricated
window-shaped result, silently corrupting downstream logic for unrelated
methods.

Changes:
- pending_requests now stores (future, method) tuples instead of bare futures
- send_raw stores the method name with the future
- _handle_messages unpacks (future, request_method) and checks
  request_method == 'Browser.getWindowForTarget' before suppressing -32000
- Exception handlers updated to unpack (future, _) tuples
@FaisalFehad

Copy link
Copy Markdown
Author

@cubic-dev-ai Issue addressed in commit 1e7577e.

The -32000 suppression is now scoped to Browser.getWindowForTarget only.

Changes:

  1. pending_requests now stores (future, method) tuples instead of bare futures
  2. send_raw() stores the CDP method name alongside each future
  3. _handle_messages() unpacks (future, request_method) and only suppresses -32000 when request_method == "Browser.getWindowForTarget"
  4. Exception handlers updated to unpack (future, _) tuples

No other CDP methods will receive fabricated window-shaped results.

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 1, 2026

Copy link
Copy Markdown

@cubic-dev-ai Issue addressed in commit 1e7577e.

The -32000 suppression is now scoped to Browser.getWindowForTarget only.

Changes:
...

@FaisalFehad Looks good — the scoping change you made in commit 1e7577e cleanly addresses the concern about blanket -32000 suppression.

A couple of observations on the implementation:

What works well:

  • Storing (future, method) tuples instead of separate tracking dicts keeps it simple — one data structure, no sync issues.
  • The guard condition is tight: both isinstance(error, dict) and error.get("code") == -32000 before checking the method name. That avoids any AttributeError if CDP ever returns a non-dict error shape.
  • The info log on suppression is at logger.info which is appropriate — this is an unusual event worth knowing about in production logs.

One minor thing worth considering:

The default bounds you return (width: 1920, height: 1080) hardcode a specific viewport. If the caller actually uses those bounds (e.g., for positioning or sizing), you could get a brief visual glitch. A safer alternative might be raising the exception after a short retry, but that's a much bigger change and probably not worth it — in practice, these window-less targets get their binding immediately, so the fake result is consumed and discarded before anything sees it. Just worth being aware of if you ever see weird initial sizes in downstream code.

Otherwise, this is a solid fix for a nasty race condition. The method-scoped suppression keeps it surgical while the race window stays open.

Tip: get faster answers by chatting with cubic’s review copilot in the review UI

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