{"record":{"id":"8b40f9788ba67d86","repo":"grafana/k6","slug":"sending-a-message-to-browser-w","errorCode":null,"errorMessage":"sending a message to browser: %w","messagePattern":"sending a message to browser: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/connection.go","lineNumber":480,"sourceCode":"\tmsg := &cdproto.Message{\n\t\tID:        c.msgIDGen.newID(),\n\t\tSessionID: sid,\n\t\tMethod:    cdproto.MethodType(cdpruntime.CommandRunIfWaitingForDebugger),\n\t}\n\terr := c.send(c.ctx, msg, nil, nil)\n\tif err != nil {\n\t\tc.logger.Errorf(\"Connection:stopWaitingForDebugger\", \"sid:%v wsURL:%q, err:%v\", sid, c.wsURL, err)\n\t}\n}\n\nfunc (c *Connection) send(\n\tctx context.Context, msg *cdproto.Message, recvCh chan *cdproto.Message, res any,\n) error {\n\tselect {\n\tcase c.sendCh <- msg:\n\tcase err := <-c.errorCh:\n\t\tc.logger.Debugf(\"Connection:send:<-c.errorCh\", \"wsURL:%q sid:%v, err:%v\", c.wsURL, msg.SessionID, err)\n\t\treturn fmt.Errorf(\"sending a message to browser: %w\", err)\n\tcase code := <-c.closeCh:\n\t\tc.logger.Debugf(\"Connection:send:<-c.closeCh\", \"wsURL:%q sid:%v, websocket code:%v\", c.wsURL, msg.SessionID, code)\n\t\t_ = c.close(code)\n\t\treturn fmt.Errorf(\"closing communication with browser: %w\", &websocket.CloseError{Code: code})\n\tcase <-ctx.Done():\n\t\tc.logger.Debugf(\"Connection:send:<-ctx.Done\", \"wsURL:%q sid:%v err:%v\", c.wsURL, msg.SessionID, ContextErr(ctx))\n\t\treturn nil\n\tcase <-c.done:\n\t\tc.logger.Debugf(\"Connection:send:<-c.done\", \"wsURL:%q sid:%v\", c.wsURL, msg.SessionID)\n\t\treturn nil\n\t}\n\n\t// Block waiting for response.\n\tif recvCh == nil {\n\t\treturn nil\n\t}\n\ttid := c.findTargetIDForLog(msg.SessionID)\n\tselect {","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/connection.go#L462-L498","documentation":"Connection.send publishes a CDP message and simultaneously watches errorCh; if the sendLoop reports a websocket write error (broken/aborted connection), send fails with this wrapper. It means the command never (reliably) reached the browser — the transport was already broken when k6 tried to write.","triggerScenarios":"Any CDP operation (page.goto, click, evaluate, screenshot…) issued after or exactly while the websocket breaks: browser crashed mid-test, connection reset by the remote browser, or network interruption to a remote browser.","commonSituations":"Browser OOM-killed mid-scenario; remote browser pod evicted; long-running tests where an idle connection is reaped by a load balancer; operations attempted after a prior unnoticed close.","solutions":["Check whether the browser process is still alive and consult its logs for the crash reason.","For remote browsers, keepalive/tune LB idle timeouts so ws connections are not reaped mid-test.","Make scripts resilient: catch the error, re-acquire page/context, and retry the logical action once.","If it recurs at a specific step, capture K6_BROWSER_ENABLE_DEBUGGING output to see which CDP domain call breaks."],"exampleFix":"// before\nawait page.goto('https://example.com');  // throws 'sending a message to browser: ...' if ws broke\n\n// after\nasync function gotoRetry(page, url, tries = 2) {\n  for (let i = 0; i < tries; i++) {\n    try { return await page.goto(url); }\n    catch (e) {\n      if (i === tries - 1) throw e;\n      await sleep(1000);\n    }\n  }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await page.goto(url, { waitUntil: 'load' });\n} catch (e) {\n  if (/sending a message to browser/.test(String(e))) {\n    // transport was broken: re-open a page (or context) and retry the logical step once\n    const p2 = await browser.newPage();\n    await p2.goto(url, { waitUntil: 'load' });\n  } else {\n    throw e;\n  }\n}","preventionTips":["Don't reuse a page after any error mentioning the connection: re-create page/context first.","For remote browsers, keep connections warm or lower LB idle timeouts' impact with periodic activity.","Cap action timeouts so a dead connection fails fast instead of hanging sends."],"tags":["websocket","connection","cdp","send","browser"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}