{"record":{"id":"c58f4f12f91c39be","repo":"grafana/k6","slug":"failed-to-dial-websocket-at-s-w","errorCode":null,"errorMessage":"failed to dial websocket at %s: %w","messagePattern":"failed to dial websocket at (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/js/modules/k6/browser/common/connection.go","lineNumber":169,"sourceCode":"\tvar tlsConfig *tls.Config\n\twsd := websocket.Dialer{\n\t\tHandshakeTimeout: time.Second * 60,\n\t\tProxy:            http.ProxyFromEnvironment, // TODO(fix): use proxy settings from launch options\n\t\tTLSClientConfig:  tlsConfig,\n\t\tWriteBufferSize:  wsWriteBufferSize,\n\t\tReadBufferSize:   wsWriteBufferSize,\n\t}\n\n\tctx, cancelCtx := context.WithCancelCause(ctx)\n\n\tconn, response, connErr := wsd.DialContext(ctx, wsURL, header)\n\tif response != nil {\n\t\tdefer func() {\n\t\t\t_ = response.Body.Close()\n\t\t}()\n\t}\n\tif connErr != nil {\n\t\tcancelCtx(fmt.Errorf(\"failed to dial websocket at %s: %w\", wsURL, connErr))\n\t\treturn nil, connErr\n\t}\n\n\tc := Connection{\n\t\tBaseEventEmitter:         NewBaseEventEmitter(ctx),\n\t\tctx:                      ctx,\n\t\tcancelCtx:                cancelCtx,\n\t\twsURL:                    wsURL,\n\t\tlogger:                   logger,\n\t\tconn:                     conn,\n\t\tsendCh:                   make(chan *cdproto.Message, 32), // Avoid blocking in Execute\n\t\trecvCh:                   make(chan *cdproto.Message),\n\t\tcloseCh:                  make(chan int),\n\t\terrorCh:                  make(chan error),\n\t\tdone:                     make(chan struct{}),\n\t\tclosing:                  make(chan struct{}),\n\t\tmsgIDGen:                 &msgID{},\n\t\tsessions:                 make(map[target.SessionID]*Session),","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/connection.go#L151-L187","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the exact URL from the message: curl http://host:port/json/version must return the webSocketDebuggerUrl.","Ensure the remote browser is launched with --remote-debugging-port=<port> and is reachable (telnet/nc).","Use the full ws:// URL including the /devtools/browser/... path, not just host:port.","Open the port in firewall/NetworkPolicy rules and bypass ws-blocking proxies.","If the remote is started as part of the pipeline, wait for its readiness probe before launching k6."],"exampleFix":"# before\nexport K6_BROWSER_REMOTE_URL=ws://browser:3000\nk6 run test.js  # failed to dial websocket\n\n# after\n# browser service exposes 9222 and is ready\ncurl http://browser:9222/json/version   # returns webSocketDebuggerUrl\nexport K6_BROWSER_REMOTE_URL=ws://browser:9222/devtools/browser/<id>\nk6 run test.js","handlingStrategy":"retry","validationCode":"# before starting k6, confirm the remote browser answers and grab a fresh ws URL\nBASE=\"${K6_BROWSER_REMOTE_URL%%/devtools/*}\"\nBASE=\"${BASE#ws://}\"; BASE=\"${BASE#http://}\"\nWS=$(curl -fsS \"http://$BASE/json/version\" | sed -n 's/.*\"webSocketDebuggerUrl\": *\"\\([^\"]*\\)\".*/\\1/p')\n[ -n \"$WS\" ] || { echo \"remote browser not reachable at $BASE\" >&2; exit 1; }\nexport K6_BROWSER_REMOTE_URL=\"$WS\"","typeGuard":null,"tryCatchPattern":"try {\n  await browser.newPage();\n} catch (e) {\n  if (/failed to dial websocket/.test(String(e))) {\n    // remote browser not ready — surface a clear remediation instead of a raw dial error\n    throw new Error('Remote browser unreachable: verify it runs with --remote-debugging-port and the URL is ws://host:port/devtools/browser/<id>');\n  }\n  throw e;\n}","preventionTips":["Always health-check the remote browser (/json/version) before launching k6 against it.","Use readiness probes when the remote browser starts in the same pipeline.","Keep LB/proxy idle timeouts above the test duration so ws connections are not reaped."],"tags":["websocket","connection","remote-browser","network","configuration"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}