grafana/k6 · error

connecting to Chromium over CDP: %w

Error message

connecting to Chromium over CDP: %w

What it means

browser.chromium.connectOverCDP(wsEndpoint) attaches k6 to a user-managed Chromium over the DevTools Protocol: it validates the ws:// URL up front, initializes browser options from K6_BROWSER_* env vars, then dials the endpoint; any dial/handshake failure is wrapped as 'connecting to Chromium over CDP' (with a UserFriendlyError that surfaces timeout context). It exists so a browser scenario is not required - you bring your own Chromium and pass its WebSocket debugger URL.

Source

Thrown at internal/js/modules/k6/browser/browser/chromium_mapping.go:28

)

// mapChromium maps the Chromium browser type API to the JS module.
func mapChromium(vu moduleVU, bt *chromium.BrowserType) mapping {
	return mapping{
		"connectOverCDP": func(wsEndpoint string) *sobek.Promise {
			return promise(vu, func() (any, error) {
				iter := vu.State().Iteration

				// Clone the BrowserType for this call so concurrent
				// connectOverCDP calls in the same iteration (e.g., via
				// Promise.all) don't race on its mutable state.
				connBT := bt.Clone()

				// Link the connection to the iteration trace and connect.
				tracedCtx := vu.startConnectTrace(vu.Context(), iter)
				b, err := connBT.ConnectOverCDP(tracedCtx, wsEndpoint)
				if err != nil {
					return nil, fmt.Errorf("connecting to Chromium over CDP: %w", err)
				}

				// Register for guaranteed cleanup at IterEnd / Exit.
				vu.trackUserManagedBrowser(iter, b)

				return mapBrowser(vu, func() (*common.Browser, error) {
					return b, nil
				}), nil
			})
		},
	}
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Start Chromium with remote debugging: chromium --headless --remote-debugging-port=9222 --no-sandbox (as needed)
  2. Fetch the browser-level WebSocket URL from http://127.0.0.1:9222/json/version and pass its webSocketDebuggerUrl (ws://...) to connectOverCDP
  3. Verify reachability first: curl http://HOST:9222/json/version from the same host/network k6 runs on
  4. If the handshake times out, raise K6_BROWSER_TIMEOUT and confirm the browser process stays alive for the whole iteration

Example fix

// before: http endpoint / guessed ws URL
import browser from 'k6/browser';
export default async () => {
  const b = browser.chromium.connectOverCDP('http://127.0.0.1:9222'); // wrong scheme
};

// after: resolve the real browser ws endpoint, then connect
import http from 'k6/http';
import browser from 'k6/browser';
const ver = http.get('http://127.0.0.1:9222/json/version').json();
export default async () => {
  const b = browser.chromium.connectOverCDP(ver.webSocketDebuggerUrl);
  const page = await b.newPage();
  await page.goto('https://k6.io');
};
Defensive patterns

Strategy: validation

Validate before calling

// verify the debugger is reachable and resolve the browser ws URL before connecting
import http from 'k6/http';
const res = http.get('http://127.0.0.1:9222/json/version');
if (res.status !== 200) throw new Error(`CDP endpoint unreachable (HTTP ${res.status})`);
const ws = res.json().webSocketDebuggerUrl;
if (typeof ws !== 'string' || !ws.startsWith('ws://')) throw new Error('no valid webSocketDebuggerUrl');
// pass `ws` to browser.chromium.connectOverCDP inside the default function

Type guard

function isWsEndpoint(v) {
  return typeof v === 'string' && /^wss?:\/\/[^\s/]+(\/\S*)?$/.test(v);
}

Try / catch

try {
  const b = browser.chromium.connectOverCDP(ws);
} catch (e) {
  const msg = String(e);
  if (msg.includes('WebSocket endpoint')) throw new Error('fix the ws:// URL: ' + msg);
  if (msg.includes('timed out') || msg.includes('timeout')) throw new Error('browser not reachable - check port and K6_BROWSER_TIMEOUT');
  if (msg.includes('connecting to Chromium over CDP')) throw new Error('handshake failed - is Chromium running with --remote-debugging-port?');
  throw e;
}

Prevention

When it happens

Trigger: wsEndpoint empty, not a ws:// URL, or malformed (caught by validateWSEndpoint); passing an http:// endpoint where a ws:// webSocketDebuggerUrl is required; Chromium not started with --remote-debugging-port; wrong host/port; debugger reachable but the browser exits or the WebSocket handshake times out (K6_BROWSER_TIMEOUT).

Common situations: Connecting k6 to containerized or remote Chromium without publishing the debug port; grabbing the URL from the wrong /json endpoint entry (page target instead of browser target); Chrome security restrictions on remote debugging with non-127.0.0.1 addresses; firewalled CI runners.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/e3f552e7fc0b9c89. Report an issue: GitHub.