grafana/k6 · critical

failed to dial websocket at %s: %w

Error message

failed to dial websocket at %s: %w

What it means

Connection dialing (websocket.Dialer.DialContext) to the DevTools wsURL failed, so k6 cancels the connection context with this cause while returning the raw dial error. The %s is the exact websocket URL attempted (local browser endpoint or a K6_BROWSER_REMOTE_URL).

Source

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

	var tlsConfig *tls.Config
	wsd := websocket.Dialer{
		HandshakeTimeout: time.Second * 60,
		Proxy:            http.ProxyFromEnvironment, // TODO(fix): use proxy settings from launch options
		TLSClientConfig:  tlsConfig,
		WriteBufferSize:  wsWriteBufferSize,
		ReadBufferSize:   wsWriteBufferSize,
	}

	ctx, cancelCtx := context.WithCancelCause(ctx)

	conn, response, connErr := wsd.DialContext(ctx, wsURL, header)
	if response != nil {
		defer func() {
			_ = response.Body.Close()
		}()
	}
	if connErr != nil {
		cancelCtx(fmt.Errorf("failed to dial websocket at %s: %w", wsURL, connErr))
		return nil, connErr
	}

	c := Connection{
		BaseEventEmitter:         NewBaseEventEmitter(ctx),
		ctx:                      ctx,
		cancelCtx:                cancelCtx,
		wsURL:                    wsURL,
		logger:                   logger,
		conn:                     conn,
		sendCh:                   make(chan *cdproto.Message, 32), // Avoid blocking in Execute
		recvCh:                   make(chan *cdproto.Message),
		closeCh:                  make(chan int),
		errorCh:                  make(chan error),
		done:                     make(chan struct{}),
		closing:                  make(chan struct{}),
		msgIDGen:                 &msgID{},
		sessions:                 make(map[target.SessionID]*Session),

View on GitHub (pinned to 93accf6570)

Solutions

  1. Verify the exact URL from the message: curl http://host:port/json/version must return the webSocketDebuggerUrl.
  2. Ensure the remote browser is launched with --remote-debugging-port=<port> and is reachable (telnet/nc).
  3. Use the full ws:// URL including the /devtools/browser/... path, not just host:port.
  4. Open the port in firewall/NetworkPolicy rules and bypass ws-blocking proxies.
  5. If the remote is started as part of the pipeline, wait for its readiness probe before launching k6.

Example fix

# before
export K6_BROWSER_REMOTE_URL=ws://browser:3000
k6 run test.js  # failed to dial websocket

# after
# browser service exposes 9222 and is ready
curl http://browser:9222/json/version   # returns webSocketDebuggerUrl
export K6_BROWSER_REMOTE_URL=ws://browser:9222/devtools/browser/<id>
k6 run test.js
Defensive patterns

Strategy: retry

Validate before calling

# before starting k6, confirm the remote browser answers and grab a fresh ws URL
BASE="${K6_BROWSER_REMOTE_URL%%/devtools/*}"
BASE="${BASE#ws://}"; BASE="${BASE#http://}"
WS=$(curl -fsS "http://$BASE/json/version" | sed -n 's/.*"webSocketDebuggerUrl": *"\([^"]*\)".*/\1/p')
[ -n "$WS" ] || { echo "remote browser not reachable at $BASE" >&2; exit 1; }
export K6_BROWSER_REMOTE_URL="$WS"

Try / catch

try {
  await browser.newPage();
} catch (e) {
  if (/failed to dial websocket/.test(String(e))) {
    // remote browser not ready — surface a clear remediation instead of a raw dial error
    throw new Error('Remote browser unreachable: verify it runs with --remote-debugging-port and the URL is ws://host:port/devtools/browser/<id>');
  }
  throw e;
}

Prevention

When it happens

Trigger: Connecting to a remote browser (K6_BROWSER_REMOTE_URL) that is not running, not listening on that port, firewalled, or requires auth; the locally launched browser's DevTools endpoint dying between discovery and dial; a proxy interfering with ws:// upgrades.

Common situations: Wrong or stale K6_BROWSER_REMOTE_URL (http:// vs ws://, missing /devtools/browser/<id> path); remote Chrome not started with --remote-debugging-port; k8s NetworkPolicy or corporate firewall blocking the port; remote browser started per-test but k6 dialing before it is ready.

Related errors


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