fix(browser): fall back to direct CDP connection for remote profiles#15845
fix(browser): fall back to direct CDP connection for remote profiles#15845kushagrob wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Remote CDP providers like Browserbase don't serve the /json/version HTTP endpoint that ensureBrowserAvailable() uses as a pre-flight check. This causes all remote CDP connections to fail with "Remote CDP is not reachable" even though the browser is fully available via WebSocket. connectBrowser() already handles this case gracefully — it falls back to using the raw cdpUrl when getChromeWebSocketUrl() returns null. But ensureBrowserAvailable() throws before connectBrowser() is ever called. This fix adds a direct Playwright connectOverCDP fallback in the !httpReachable && remoteCdp path, using the existing listPagesViaPlaywright() pattern already used by listTabs() for remote profiles. If the direct connection succeeds, the browser is available and execution continues normally. Fixes openclaw#15582 Related: openclaw#5690 Co-Authored-By: Claude Opus 4.6 <[email protected]>
| // Remote CDP providers (e.g., Browserbase) may not serve /json/version | ||
| // but are still reachable via direct Playwright connectOverCDP. | ||
| // Try a direct connection as a fallback before giving up. | ||
| if (remoteCdp) { | ||
| try { | ||
| const mod = await getPwAiModule({ mode: "strict" }); | ||
| const fn = (mod as Partial<PwAiModule> | null)?.listPagesViaPlaywright; | ||
| if (typeof fn === "function") { | ||
| await fn({ cdpUrl: profile.cdpUrl }); | ||
| return; | ||
| } | ||
| } catch { | ||
| // Direct CDP connection also failed — fall through to throw | ||
| } |
There was a problem hiding this comment.
Probe creates cached CDP
This fallback calls listPagesViaPlaywright(), which establishes (and globally caches) a Playwright chromium.connectOverCDP connection (src/browser/pw-session.ts:320-366). That means ensureBrowserAvailable() can now create a long-lived CDP connection on remoteCdp && !httpReachable, and it also inherits connectBrowser()’s multi-retry 5s/7s/9s timeouts (src/browser/pw-session.ts:331-353), turning what used to be a fast reachability failure into a potentially long, stateful side-effect. Consider using a non-cached / short-timeout connectivity check here, or explicitly ensuring any connection created for this probe is closed/doesn’t populate the shared cache.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/server-context.ts
Line: 317:330
Comment:
**Probe creates cached CDP**
This fallback calls `listPagesViaPlaywright()`, which establishes (and globally caches) a Playwright `chromium.connectOverCDP` connection (`src/browser/pw-session.ts:320-366`). That means `ensureBrowserAvailable()` can now create a long-lived CDP connection on `remoteCdp && !httpReachable`, and it also inherits `connectBrowser()`’s multi-retry 5s/7s/9s timeouts (`src/browser/pw-session.ts:331-353`), turning what used to be a fast reachability failure into a potentially long, stateful side-effect. Consider using a non-cached / short-timeout connectivity check here, or explicitly ensuring any connection created for this probe is closed/doesn’t populate the shared cache.
How can I resolve this? If you propose a fix, please make it concise.| try { | ||
| const mod = await getPwAiModule({ mode: "strict" }); | ||
| const fn = (mod as Partial<PwAiModule> | null)?.listPagesViaPlaywright; | ||
| if (typeof fn === "function") { | ||
| await fn({ cdpUrl: profile.cdpUrl }); | ||
| return; | ||
| } | ||
| } catch { | ||
| // Direct CDP connection also failed — fall through to throw | ||
| } | ||
| } |
There was a problem hiding this comment.
Lost fallback error context
The catch {} here swallows the real failure reason from listPagesViaPlaywright() (auth/TLS/DNS/protocol issues) and then falls through to throw the generic “Remote CDP … not reachable” message. When this fallback path is hit, it will be much harder to diagnose why the direct CDP attempt failed. At minimum, consider including the caught error message (or chaining via new Error(msg, { cause: err })) in the thrown error for the remoteCdp case.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/server-context.ts
Line: 321:331
Comment:
**Lost fallback error context**
The `catch {}` here swallows the real failure reason from `listPagesViaPlaywright()` (auth/TLS/DNS/protocol issues) and then falls through to throw the generic “Remote CDP … not reachable” message. When this fallback path is hit, it will be much harder to diagnose why the direct CDP attempt failed. At minimum, consider including the caught error message (or chaining via `new Error(msg, { cause: err })`) in the thrown error for the `remoteCdp` case.
How can I resolve this? If you propose a fix, please make it concise.
Summary
Remote CDP providers like Browserbase don't serve the
/json/versionHTTP endpoint thatensureBrowserAvailable()uses as a pre-flight check. This causes all remote CDP connections to these providers to fail with"Remote CDP is not reachable"even though the browser is fully available via WebSocket/CDP.connectBrowser()inpw-session.tsalready handles this case gracefully — it falls back to using the rawcdpUrlwhengetChromeWebSocketUrl()returnsnull:But
ensureBrowserAvailable()throws beforeconnectBrowser()is ever called.Fix
!httpReachable && remoteCdpbranch ofensureBrowserAvailable(), try a direct PlaywrightconnectOverCDPvialistPagesViaPlaywright()before throwinglistTabs()for remote profilesattachOnlybehaviorAffected providers
This fix unblocks remote CDP providers that use WebSocket-only connection protocols:
wss://connect.usw2.browserbase.com/?signingKey=<JWE>)Test plan
httpReachable=truepath unchanged)/json/versionsupport (e.g., Browserless) still works via fast HTTP path/json/version(e.g., Browserbase) now connects via fallbackFixes #15582
Related: #5690
Greptile Overview
Greptile Summary
This change updates
ensureBrowserAvailable()to handle remote CDP providers that don’t expose Chrome’s/json/versionHTTP endpoint by attempting a direct PlaywrightconnectOverCDP(vialistPagesViaPlaywright) before throwing “Remote CDP is not reachable”. This aligns the pre-flight behavior with existing remote-profile tab listing logic that already uses Playwright for remote CDP.Key concern: the new fallback path can create a long-lived, globally cached Playwright CDP connection (and uses multi-retry 5s+ timeouts), which turns a reachability probe into a stateful and potentially slow operation. Also, errors from the fallback are swallowed, making failures harder to debug when the fallback is exercised.
Confidence Score: 3/5
remoteCdp && !httpReachable, but it now establishes a cached Playwright CDP connection duringensureBrowserAvailable()and may block for connect retries/timeouts; the fallback also hides the underlying connection error, reducing debuggability on failure paths.Last reviewed commit: 1bb9284
(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!