grafana/k6 · error

invalid WebSocket endpoint %q: host is missing

Error message

invalid WebSocket endpoint %q: host is missing

What it means

Thrown by validateWSEndpoint (called from BrowserType.ConnectOverCDP) when the endpoint has a valid ws/wss scheme but url.Hostname() is empty, i.e. there is no host between 'ws://' and the path. This catches partially-formed CDP URLs such as 'ws:///devtools/browser/<uuid>' before any network dial is attempted, guaranteeing a precise error rather than a DNS/dial failure.

Source

Thrown at internal/js/modules/k6/browser/chromium/browser_type.go:170

// 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.
//
// The separation is important to allow for the iteration to end when k6 requires
// the iteration to end (e.g. during a SIGTERM) and unblocks k6 to then fire off
// the events which allows the connection to close.
func (b *BrowserType) Connect(ctx, vuCtx context.Context, wsEndpoint string) (*common.Browser, error) {
	vuCtx, browserOpts, logger, err := b.init(vuCtx, true, k6ext.GetScenarioOpts(b.vu.Context(), b.vu))

View on GitHub (pinned to 93accf6570)

Solutions

  1. Include an explicit host: `ws://127.0.0.1:9222/devtools/browser/<uuid>`
  2. If the URL is built dynamically, log it before calling connectOverCDP and fix the empty host variable
  3. Run curl http://<host>:<port>/json/version against the browser and copy the full webSocketDebuggerUrl verbatim
  4. Add a script-level assertion that the URL matches /^wss?://[^/]+/

Example fix

// before
const endpoint = `ws://${K6_BROWSER_HOST}/devtools/browser/${uuid}`; // K6_BROWSER_HOST unset -> 'ws:///devtools/...'
const browser = chromium.connectOverCDP(endpoint);

// after
const host = __ENV.BROWSER_HOST || '127.0.0.1';
const endpoint = `ws://${host}:9222/devtools/browser/${uuid}`;
const browser = chromium.connectOverCDP(endpoint);
Defensive patterns

Strategy: validation

Validate before calling

function buildWsEndpoint(host, port, uuid) {
  if (!host) throw new Error('browser host is required');
  return `ws://${host}:${port}/devtools/browser/${uuid}`;
}
const endpoint = buildWsEndpoint(__ENV.BROWSER_HOST, __ENV.BROWSER_PORT || '9222', uuid);
if (!/^wss?:\/\/[^\/]+/.test(endpoint)) throw new Error(`bad endpoint: ${endpoint}`);

Try / catch

try {
  const browser = chromium.connectOverCDP(endpoint);
} catch (e) {
  if (String(e.message).includes('host is missing')) {
    throw new Error(`BROWSER_HOST env var is empty; endpoint was: ${endpoint}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling chromium.connectOverCDP('ws:///devtools/browser/<uuid>') (host omitted, e.g. lost during string templating); building the URL by concatenation where the host variable is empty: `ws://${host}/devtools/...` with host unset; passing 'ws://:9222' (port without host).

Common situations: Endpoint assembled from environment variables where the host variable is missing or misspelled; CI pipelines that inject the WS path but not the host; scripts copied between environments where the host placeholder was never substituted.

Related errors


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