BigPizzaV3/CodexPlusPlus · error · anyhow::Error
browser WebSocket URL has no path
Error message
browser WebSocket URL has no path
What it means
CdpBrowserIdentity::browser_id (crates/codex-plus-core/src/cdp.rs:38) derives the browser ID from the path of the /json/version webSocketDebuggerUrl and expects the shape /devtools/browser/<id>. path_segments() returns None only for URLs that cannot be a base (opaque-path URLs, e.g. 'ws:devtools' or a bare non-hierarchical string that still parses), so this error indicates a malformed, non-hierarchical debugger URL rather than a merely empty path.
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 1f431ae49b)
Solutions
- Use the webSocketDebuggerUrl exactly as returned by the real endpoint's /json/version — do not rewrite it
- Validate the URL shape (scheme ws/wss + host + /devtools/browser/<id> path) before constructing CdpBrowserIdentity or calling browser_id()
- If you control the server, return a full hierarchical URL: ws://127.0.0.1:9222/devtools/browser/<uuid>
Example fix
// before: opaque URL — Url::parse succeeds, path_segments() is None
let id = identity.browser_id()?; // web_socket_debugger_url = "ws:browser"
// after: full hierarchical URL from /json/version
// web_socket_debugger_url = "ws://127.0.0.1:9222/devtools/browser/<uuid>"
let id = identity.browser_id()?; // Ok("<uuid>") Defensive patterns
Strategy: validation
Validate before calling
// Require the /devtools/browser/<id> shape before deriving a browser id
let u = reqwest::Url::parse(&identity.web_socket_debugger_url)?;
ensure!(u.path_segments().is_some(), "URL has no hierarchical path");
ensure!(u.path().starts_with("/devtools/browser/"), "unexpected CDP path shape");
let id = identity.browser_id()?; Type guard
fn is_hierarchical_ws_url(url: &str) -> bool {
reqwest::Url::parse(url)
.ok()
.and_then(|u| u.path_segments().map(|mut s| s.next().is_some()))
.unwrap_or(false)
} Try / catch
match identity.browser_id() {
Ok(id) => Ok(id),
Err(e) if e.to_string().contains("no path") || e.to_string().contains("Browser ID") => {
// discard the malformed identity and re-fetch /json/version from the endpoint
refetch_browser_identity(port).await?.browser_id()
}
Err(e) => Err(e),
} Prevention
- Never rewrite or shorten webSocketDebuggerUrl strings
- In tests/mocks, return full hierarchical URLs (ws://127.0.0.1:<port>/devtools/browser/<uuid>)
- Validate the URL shape at the trust boundary before persisting identities
When it happens
Trigger: Calling browser_id() on a CdpBrowserIdentity whose web_socket_debugger_url parses via Url::parse but has no hierarchical path — hand-built strings like "ws:browser" or data produced by a non-Chromium endpoint that mimics /json/version with an opaque ws URL.
Common situations: A mock/stub CDP endpoint used in tests returning a shorthand URL; a browser fork or other DevTools-protocol server whose version payload differs from Chrome's; string munging (schema prefix stripping) that turns ws://host/devtools/... into an opaque form.
Related errors
- CDP WebSocket URL must include an explicit port
- CDP WebSocket URL has no host
- CDP WebSocket URL must include an explicit port
- Page.captureScreenshot returned no image data
- Codex injection failed
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/022373cb4ff53a18.
Report an issue: GitHub.