karatelabs/karate · error · DriverException
not a devtools websocket url
Error message
not a devtools websocket url: {browserWebSocketUrl} What it means
pageWsUrlFrom derives a page WebSocket endpoint by rewriting the browser's /devtools/browser/<id> URL to /devtools/page/<targetId>. If the given URL does not contain '/devtools/', it is not a Chromium devtools browser endpoint and the rewrite is impossible, so this DriverException is thrown.
Solutions
- Use the exact webSocketDebuggerUrl reported by http://host:port/json/version (it always contains /devtools/browser/)
- If behind a proxy that rewrites paths, ensure the /devtools/ prefix is preserved end-to-end
- Validate the URL starts with ws(s)://host:port/devtools/browser/ before passing it to connectNewContext
Example fix
// before String url = "ws://localhost:9222"; CdpDriver.connectNewContext(url, options); // after String url = "ws://localhost:9222/devtools/browser/" + browserId; // from /json/version CdpDriver.connectNewContext(url, options);
Defensive patterns
Strategy: validation
Validate before calling
boolean ok = url.startsWith("ws://") || url.startsWith("wss://");
ok = ok && url.contains("/devtools/browser/");
if (!ok) throw new IllegalArgumentException("not a devtools browser websocket url: " + url); Try / catch
try { return CdpDriver.connectNewContext(url, options); } catch (DriverException e) { if (e.getMessage().startsWith("not a devtools")) { url = fetchDevtoolsUrl(host, port); return CdpDriver.connectNewContext(url, options); } throw e; } Prevention
- Always source the URL from /json/version webSocketDebuggerUrl
- Never strip or rewrite the /devtools/ path segment when proxying
- Validate URL shape in config loading before tests start
When it happens
Trigger: Calling CdpDriver.connectNewContext (or anything that calls pageWsUrlFrom) with a browserWebSocketUrl lacking the '/devtools/' segment — e.g. ws://host:port alone, a raw socket path, or a vendor-specific endpoint format.
Common situations: Passing the raw debugging port URL (ws://localhost:9222) instead of the webSocketDebuggerUrl from /json/version; using a Playwright/Puppeteer-style browserWS endpoint whose path differs; typo in a configured driver URL.
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
- browser did not return a browserContextId
- browser did not return a targetId for context
- CDP connection failed readiness check
- CDP timeout for
- CDP error
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/8c65dc27cac20d89.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:584
.param("browserContextId", contextId).send();
} catch (Exception ignored) {
logger.debug("could not dispose orphan browser context: {}", contextId);
}
throw e;
}
} finally {
browser.close();
}
}
/**
* Derive the page endpoint for a target from a browser endpoint on the same browser.
* ws://host:port/devtools/browser/<id> → ws://host:port/devtools/page/<targetId>
*/
private static String pageWsUrlFrom(String browserWebSocketUrl, String targetId) {
int index = browserWebSocketUrl.indexOf("/devtools/");
if (index == -1) {
throw new DriverException("not a devtools websocket url: " + browserWebSocketUrl);
}
return browserWebSocketUrl.substring(0, index) + "/devtools/page/" + targetId;
}
/**
* Close all active drivers.
*/
public static void closeAll() {
ACTIVE.forEach(CdpDriver::quit);
}
private void initialize() {
logger.debug("initializing CDP driver");
// CRITICAL: Get main frame ID FIRST - needed by event handlers
// This works without Page.enable and ensures handlers can filter by frame
CdpResponse frameResponse = cdp.method("Page.getFrameTree").send();
mainFrameId = frameResponse.getResult("frameTree.frame.id");View on GitHub (pinned to a22eb90246)