BigPizzaV3/CodexPlusPlus · error
browser WebSocket URL has no path
Error message
browser WebSocket URL has no path
What it means
`CdpBrowserIdentity::browser_id` parses the browser's `webSocketDebuggerUrl` and extracts the Browser ID from the path segment after `/devtools/browser/`. If the URL parses but has no path segments at all (`url.path_segments()` returns None — only possible for URLs without a path), this error is thrown. A URL with a path but wrong shape yields the sibling 'no Browser ID' error instead.
Solutions
- Use the exact `webSocketDebuggerUrl` from http://127.0.0.1:<port>/json/version
- Re-fetch /json/version; the browser may have restarted with a new Browser ID
- Validate the URL matches /devtools/browser/<id> before constructing CdpBrowserIdentity
- Check any code that rewrites the host/port portion of the URL preserves the path
Example fix
// before
let identity = CdpBrowserIdentity { web_socket_debugger_url: "ws://127.0.0.1:9222".into() };
// after
let ws_url = fetch_json_version(port)?.webSocketDebuggerUrl; // ws://127.0.0.1:9222/devtools/browser/<id>
let identity = CdpBrowserIdentity { web_socket_debugger_url: ws_url }; Defensive patterns
Strategy: validation
Validate before calling
let u = reqwest::Url::parse(&ws_url)?;
let segs: Vec<_> = u.path_segments().map(|s| s.collect()).unwrap_or_default();
if !(segs.len() == 3 && segs[0] == "devtools" && segs[1] == "browser" && !segs[2].is_empty()) {
return Err(anyhow!("not a valid browser WebSocket URL: {ws_url}"));
} Try / catch
match identity.browser_id() {
Err(e) if e.to_string().contains("no path") || e.to_string().contains("no Browser ID") => {
let fresh = fetch_web_socket_debugger_url(port)?; // re-query /json/version
retry_with(fresh)
}
other => other,
} Prevention
- Take webSocketDebuggerUrl directly from /json/version, never hand-built
- Re-fetch after browser restarts (Browser ID changes)
- Preserve the path when rewriting host/port
When it happens
Trigger: Calling `browser_id()` on an identity whose `web_socket_debugger_url` is like `ws://127.0.0.1:9222` with no path, or an empty-path URL from a malformed /json/version response.
Common situations: Chrome's /json/version returned a truncated webSocketDebuggerUrl, a proxy stripped the path, or the field was set from user input rather than the real DevTools response.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- CDP WebSocket URL must include an explicit port
- Page.captureScreenshot returned no image data
- browser WebSocket URL has no Browser ID
- CDP command id failed
- CDP websocket closed before response for
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/022373cb4ff53a18.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/cdp.rs:38
#[serde(default, rename = "webSocketDebuggerUrl")]
pub web_socket_debugger_url: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct CdpBrowserIdentity {
#[serde(rename = "Browser")]
pub browser: String,
#[serde(rename = "webSocketDebuggerUrl")]
pub web_socket_debugger_url: String,
}
impl CdpBrowserIdentity {
pub fn browser_id(&self) -> anyhow::Result<String> {
let url = reqwest::Url::parse(&self.web_socket_debugger_url)
.context("invalid browser WebSocket URL")?;
let mut segments = url
.path_segments()
.ok_or_else(|| anyhow::anyhow!("browser WebSocket URL has no path"))?;
match (segments.next(), segments.next(), segments.next()) {
(Some("devtools"), Some("browser"), Some(id)) if !id.is_empty() => Ok(id.to_string()),
_ => bail!("browser WebSocket URL has no Browser ID"),
}
}
}
/// Returns whether the requested loopback port exposes a CDP target list.
pub(crate) fn endpoint_available(debug_port: u16) -> bool {
[
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), debug_port),
SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), debug_port),
]
.into_iter()
.any(|address| probe_endpoint(address, debug_port))
}
fn probe_endpoint(address: SocketAddr, debug_port: u16) -> bool {View on GitHub (pinned to b1ed92e5e4)