grafana/k6 · error

connecting to browser DevTools URL: %w

Error message

connecting to browser DevTools URL: %w

What it means

Returned by Browser.connect() (called from common.NewBrowser during both local launch and remote connect) when NewConnection fails to establish the CDP WebSocket connection to the browser process's DevTools URL. This sits below the 'connecting to browser'/'launching browser' wrappers: the process object exists, but the actual protocol channel to it could not be opened.

Source

Thrown at internal/js/modules/k6/browser/common/browser.go:160

func (b *Browser) connect() error {
	b.logger.Debugf("Browser:connect", "wsURL:%q", b.browserProc.WsURL())

	// connectionOnAttachedToTarget hooks into the connection to listen
	// for target attachment events. this way, browser can manage the
	// decision of target attachments. so that we can stop connection
	// from doing unnecessary work.
	//
	// We need the connection to shutdown when browser.Close is called.
	// This is why we're using the internal context.
	var err error
	b.conn, err = NewConnection(
		b.browserCtx,
		b.browserProc.WsURL(),
		b.logger,
		b.connectionOnAttachedToTarget,
	)
	if err != nil {
		return fmt.Errorf("connecting to browser DevTools URL: %w", err)
	}

	// We don't need to lock this because `connect()` is called only in NewBrowser
	b.defaultContext, err = NewBrowserContext(b.vuCtx, b, "", DefaultBrowserContextOptions(), b.logger)
	if err != nil {
		return fmt.Errorf("browser connect: %w", err)
	}
	b.runOnClose = append(b.runOnClose, b.defaultContext.cleanup)

	return b.initEvents()
}

func (b *Browser) disposeContext(id cdp.BrowserContextID) error {
	b.logger.Debugf("Browser:disposeContext", "bctxid:%v", id)

	action := target.DisposeBrowserContext(id)
	if err := action.Do(cdp.WithExecutor(b.vuCtx, b.conn)); err != nil {
		return fmt.Errorf("disposing browser context ID %s: %w", id, err)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Check whether the browser process is actually alive at connect time (docker logs / process list); a dead process is the most common cause
  2. Increase K6_BROWSER_TIMEOUT to cover slow handshakes
  3. For remote setups, re-discover the wsURL (GET /json/version) immediately before connecting instead of caching it
  4. Ensure proxies/LBs between k6 and the browser support long-lived WebSocket connections
  5. Enable K6_BROWSER_DEBUG=true to log the exact wsURL and dial error

Example fix

# before
K6_BROWSER_TIMEOUT=10s k6 run test.js

# after
K6_BROWSER_DEBUG=true K6_BROWSER_TIMEOUT=60s k6 run test.js
Defensive patterns

Strategy: retry

Try / catch

try {
  const browser = chromium.launch(); // or connectOverCDP(wsUrl)
} catch (e) {
  const m = String(e.message);
  if (m.includes('connecting to browser DevTools URL')) {
    // process existed but the CDP WS dial failed: check process liveness / wsURL freshness
    console.error('CDP dial failed; verify the browser process is alive and the wsURL is current:', m);
  }
  throw e;
}

Prevention

When it happens

Trigger: Chrome process started but crashed before its DevTools socket accepted the connection; the wsURL reported by the process is stale or wrong (remote browser restarted and the old endpoint is served no longer); network/proxy blocks the second WebSocket; TLS mismatch on wss endpoints; handshake exceeds the browser timeout.

Common situations: Chrome dying silently during startup (OOM, sandbox denial) after the process handle exists; remote browser pools recycling instances between wsURL discovery and connect; corporate proxies terminating or buffering WebSockets; Docker port-mapping changes invalidating a previously returned wsURL.

Related errors


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