grafana/k6 · critical
getting browser version information
Error message
getting browser version information: %w
What it means
fetchVersion calls Browser.getVersion over CDP right after connecting to fill protocol/product/user-agent info (used by Version()/UserAgent()). This error wraps that call failing, meaning the connection died immediately after the handshake or the endpoint is not behaving like a Chromium browser-level DevTools endpoint.
Solutions
- Fetch http://host:port/json/version and use its webSocketDebuggerUrl verbatim — old session URLs are invalid
- Confirm the browser process stays alive across the connect (check logs/exit code)
- Verify the endpoint is a Chromium browser-level DevTools endpoint, not a page target or WebDriver endpoint
- Match the Chromium/headless-shell version to what the k6 browser module supports
Example fix
# before K6_BROWSER_WS_URL=ws://localhost:9222/devtools/page/abc123 k6 run s.js # page-level endpoint # after curl -s http://localhost:9222/json/version | jq -r .webSocketDebuggerUrl K6_BROWSER_WS_URL=<browser-level url> k6 run s.js
Defensive patterns
Strategy: validation
Validate before calling
// Preflight the remote DevTools endpoint and pin the fresh browser-level URL
const res = http.get(`${__ENV.K6_BROWSER_CDP}/json/version`);
if (res.status !== 200) throw new Error('CDP endpoint not serving /json/version');
const wsUrl = res.json('webSocketDebuggerUrl');
if (!wsUrl || !wsUrl.includes('/devtools/browser/'))
throw new Error('webSocketDebuggerUrl is not a browser-level endpoint');
__ENV.K6_BROWSER_WS_URL = wsUrl; Try / catch
try {
browser.userAgent(); // triggers version fetch
} catch (e) {
if (String(e).includes('getting browser version information')) {
// refetch /json/version, relaunch with fresh URL, retry once
}
} Prevention
- Never hardcode WS URLs across runs; always resolve them via /json/version
- Use browser-level (/devtools/browser/...) endpoints only
- Health-check remote browsers before the run
When it happens
Trigger: The WS URL pointing at a page-level endpoint (/devtools/page/<id>) or non-browser service; a stale ws URL from a previous browser session; the browser process crashing or the proxy dropping the session right after connect.
Common situations: K6_BROWSER_WS_URL copied from an old run or pointing at Chrome's HTTP port instead of the webSocketDebuggerUrl; browserless/CDP proxies terminating sessions; protocol version mismatch with unusual Chromium builds.
Related errors
- internal error while auto-attaching to browser pages
- internal error while getting browser target info
- adding init script to browser context
- can't fetch the page for unknown reason
- cannot set cookies
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/723f7b2cc6bff4ba.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/common/browser.go:807
product := b.version.product
_, after, ok := strings.Cut(product, "/")
if !ok {
return product
}
return after
}
// fetchVersion returns the browser version information.
func (b *Browser) fetchVersion() (browserVersion, error) {
var (
bv browserVersion
err error
)
bv.protocolVersion, bv.product, bv.revision, bv.userAgent, bv.jsVersion, err = cdpbrowser.
GetVersion().
Do(cdp.WithExecutor(b.vuCtx, b.conn))
if err != nil {
return browserVersion{}, fmt.Errorf("getting browser version information: %w", err)
}
// Adjust the user agent to remove the headless part.
//
// Including Headless might cause issues with some websites that treat headless
// browsers differently. Later on, [BrowserContext] will set the user agent to
// this user agent if not set by the user. This will force [FrameSession] to
// set the user agent to the browser's user agent.
//
// Doing this here provides a consistent user agent across all browser contexts.
// Also, it makes it consistent to query the user agent from the browser.
if b.browserOpts.Headless {
bv.userAgent = strings.ReplaceAll(bv.userAgent, "Headless", "")
}
return bv, nil
}
View on GitHub (pinned to 3fcf5388d7)