grafana/k6 · error
invalid WebSocket endpoint %q: scheme must be ws or wss, got
Error message
invalid WebSocket endpoint %q: scheme must be ws or wss, got %q
What it means
Thrown by validateWSEndpoint (called from BrowserType.ConnectOverCDP) when the WebSocket endpoint string parses as a URL but its scheme is neither 'ws' nor 'wss'. k6 requires a raw CDP WebSocket URL (the webSocketDebuggerUrl returned by Chrome's /json/version endpoint), so an http:// DevTools HTTP URL or a bare host:port is rejected up front with a clear message instead of a confusing lower-level handshake failure.
Source
Thrown at internal/js/modules/k6/browser/chromium/browser_type.go:164
return bp, nil
}
// validateWSEndpoint returns an error if wsEndpoint is not a usable CDP
// WebSocket URL, catching common mistakes (an empty value, a non-ws scheme,
// a missing host, etc.) before we attempt to connect.
func validateWSEndpoint(wsEndpoint string) error {
if strings.TrimSpace(wsEndpoint) == "" {
return errors.New("WebSocket endpoint cannot be empty")
}
u, err := url.Parse(wsEndpoint)
if err != nil {
return fmt.Errorf("invalid WebSocket endpoint %q: %w", wsEndpoint, err)
}
if u.Scheme != "ws" && u.Scheme != "wss" {
return fmt.Errorf(
"invalid WebSocket endpoint %q: scheme must be ws or wss, got %q", wsEndpoint, u.Scheme,
)
}
if u.Hostname() == "" {
return fmt.Errorf("invalid WebSocket endpoint %q: host is missing", wsEndpoint)
}
return nil
}
// Connect attaches k6 browser to an existing browser instance.
//
// vuCtx is the context coming from the VU itself. The k6 vu/iteration controls
// its lifecycle.
//
// context.background() is used when connecting to an instance of chromium. The
// connection lifecycle should be handled by the k6 event system.View on GitHub (pinned to 93accf6570)
Solutions
- Use the WebSocket URL from Chrome: run curl http://<host>:9222/json/version and pass the value of its webSocketDebuggerUrl field (e.g. ws://127.0.0.1:9222/devtools/browser/<uuid>) to connectOverCDP
- Start Chrome with --remote-debugging-port=9222 (or --remote-debugging-pipe is not supported here) so it exposes a ws:// DevTools endpoint
- If your endpoint really is ws but on another scheme alias, rewrite it explicitly as ws://host:port/path before calling connectOverCDP
- Add a preflight check in the script that parses the endpoint and asserts the scheme starts with 'ws'
Example fix
// before
const browser = chromium.connectOverCDP('http://localhost:9222');
// after
const browser = chromium.connectOverCDP('ws://127.0.0.1:9222/devtools/browser/5f5a1e30-8b4f-4c2a-9d3e-1a2b3c4d5e6f'); Defensive patterns
Strategy: validation
Validate before calling
function assertWSEndpoint(urlStr) {
try {
const u = new URL(urlStr);
if (u.protocol !== 'ws:' && u.protocol !== 'wss:') {
throw new Error(`scheme must be ws or wss, got ${u.protocol}`);
}
if (!u.hostname) throw new Error('host is missing');
return true;
} catch (e) {
throw new Error(`invalid WebSocket endpoint ${urlStr}: ${e.message}`);
}
}
assertWSEndpoint(CDP_ENDPOINT); Try / catch
try {
const browser = chromium.connectOverCDP(CDP_ENDPOINT);
} catch (e) {
if (String(e.message).includes('scheme must be ws or wss')) {
console.error('Pass the webSocketDebuggerUrl from http://host:9222/json/version, not the http URL');
}
throw e;
} Prevention
- Always fetch the endpoint from /json/version's webSocketDebuggerUrl instead of hand-writing it
- Keep the endpoint in a single environment variable so it is validated in one place
- Prefer URL construction over string concatenation so the host cannot silently vanish
When it happens
Trigger: Calling chromium.connectOverCDP('http://localhost:9222') (the HTTP DevTools URL instead of the WS one); passing 'localhost:9222' or '127.0.0.1:9222' (url.Parse yields an empty scheme); passing an https:// endpoint; passing a URL with a typo like 'wss://' misspelled.
Common situations: Developer copies the http://localhost:9222 URL from Chrome's console output or from curl http://localhost:9222/json/version (they should copy the webSocketDebuggerUrl field instead); a containerized setup where only the HTTP port was exposed; scripts migrated from Puppeteer/Playwright examples that use http endpoints with browserURL.
Related errors
- invalid WebSocket endpoint %q: host is missing
- WebSocket endpoint cannot be empty
- connecting to Chromium over CDP: %w
- connecting to browser: %w
- connecting to browser DevTools URL: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/9d21f219394f0bff.
Report an issue: GitHub.