{"record":{"id":"729009407c95b77d","repo":"grafana/k6","slug":"closing-communication-with-browser-w","errorCode":null,"errorMessage":"closing communication with browser: %w","messagePattern":"closing communication with browser: %w","errorType":"exception","errorClass":"websocket.CloseError","httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/connection.go","lineNumber":484,"sourceCode":"\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 {\n\tcase msg := <-recvCh:\n\t\tvar sid target.SessionID\n\t\ttid = \"\"\n\t\tif msg != nil {","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/connection.go#L466-L502","documentation":"In Connection.send, the closeCh branch fires when a websocket close frame arrives from the browser while k6 is sending a message. k6 then runs c.close(code) and returns a *websocket.CloseError wrapped with this text, so callers see both the wrapper and the machine-readable close code.","triggerScenarios":"A CDP send racing a browser-initiated close: the page or target being destroyed (e.g. window.close, navigation replacing the target), browser shutting down, or remote browser restarting, all while a command for that session is in flight.","commonSituations":"Clicking a link/button whose handler closes the page, then issuing another action on the stale page object; auto-closing popups racing assertions; teardown concurrent with slow operations.","solutions":["Detect the shape: error messages containing 'closing communication with browser' plus a websocket.CloseError code — treat as 'target gone', not as a transient fault.","Re-locate pages/targets after any action that can navigate away or close the page (query browser.contexts().pages() again).","Await navigation/close-triggering actions so subsequent calls are not racing them.","In scripts, treat this specific error as non-retryable for the current page: abandon the page and continue the scenario."],"exampleFix":"// before\nawait page.click('#logout');       // closes the page\nawait page.screenshot();          // races target destruction\n\n// after\nawait page.click('#logout');\ntry {\n  await page.screenshot();\n} catch (e) {\n  if (!String(e).includes('closing communication with browser')) throw e;\n  // target legitimately closed; skip screenshot\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await page.click('#self-destruct');\n} catch (e) {\n  if (/closing communication with browser/.test(String(e))) {\n    // target/page was closed by the app — expected; verify via browser.contexts().pages()\n    const gone = (await browser.contexts()[0].pages()).length === 0;\n    if (!gone) throw e;\n  } else {\n    throw e;\n  }\n}","preventionTips":["Classify this error as 'target gone' and never blind-retry the same page object.","Re-enumerate pages after actions that can close or navigate targets.","Await close-triggering actions before issuing follow-up commands on the same target."],"tags":["websocket","connection","close","cdp","page-lifecycle"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}