grafana/k6 · error
WebSocket endpoint cannot be empty
Error message
WebSocket endpoint cannot be empty
What it means
Before connecting to a remote Chromium over CDP, k6 validates the WebSocket URL in validateWSEndpoint (internal/js/modules/k6/browser/chromium/browser_type.go:155). An empty or whitespace-only value fails immediately with this error instead of attempting a doomed connection. The value comes from the K6_BROWSER_WS_URL environment variable (or the wsURL browser option).
Solutions
- Provide a real CDP URL, e.g. K6_BROWSER_WS_URL='ws://localhost:9222/devtools/browser/<guid>'
- Fetch the endpoint from the browser's HTTP discovery API: curl http://host:9222/json/version and use its webSocketDebuggerUrl
- Unset the variable entirely if you want k6 to launch a local browser instead of connecting remotely
- Audit CI env plumbing for empty expansions (use ${VAR:?VAR required} in shells to fail fast)
Example fix
# before K6_BROWSER_WS_URL= k6 run script.js # after K6_BROWSER_WS_URL="$(curl -s http://localhost:9222/json/version | jq -r .webSocketDebuggerUrl)" k6 run script.js
Defensive patterns
Strategy: validation
Validate before calling
# fail fast in the shell before invoking k6
if [ -z "${K6_BROWSER_WS_URL// /}" ]; then
echo "K6_BROWSER_WS_URL is empty — set it to ws://host:9222/devtools/browser/<guid> or unset it for a local browser" >&2
exit 1
fi
k6 run script.js Prevention
- Use ${VAR:?message} expansion in shells so an unset/empty ws URL aborts immediately
- Fetch webSocketDebuggerUrl from http://host:9222/json/version instead of hand-writing the guid
- In CI, add a pre-run step that curls the browser's /json/version endpoint as a healthcheck
- Prefer K6_BROWSER_HEADLESS and ws settings in one place (env file) over scattered flags
When it happens
Trigger: Running with K6_BROWSER_WS_URL defined but empty (K6_BROWSER_WS_URL= k6 run script.js), a whitespace-only value, CI defining the variable with no value, or a docker -e K6_BROWSER_WS_URL flag without an argument; a programmatic BrowserConfig with wsURL left "" while remote connection is selected.
Common situations: CI secrets/variables not populated for the job; docker-compose files where the env var is declared but the service URL is commented out; shell expansion of an unset variable ($REMOTE_WS expanding to empty); switching a pipeline from local to remote browser without updating values.
Related errors
- can't fetch the page for unknown reason
- missing required url
- adding init script to browser context
- both K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN…
- both K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN…
AI-assisted analysis of grafana/k6@8d06114777 (2026-08-18).
Data as JSON: /api/errors/20375e15a7ad9f9f.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/chromium/browser_type.go:155
bp, err := b.connect(ctx, ctx, wsEndpoint, browserOpts, logger)
if err != nil {
err = &k6ext.UserFriendlyError{
Err: err,
Timeout: browserOpts.Timeout,
}
return nil, fmt.Errorf("%w", err)
}
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 nilView on GitHub (pinned to 8d06114777)