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

  1. 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
  2. Start Chrome with --remote-debugging-port=9222 (or --remote-debugging-pipe is not supported here) so it exposes a ws:// DevTools endpoint
  3. If your endpoint really is ws but on another scheme alias, rewrite it explicitly as ws://host:port/path before calling connectOverCDP
  4. 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

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


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