{"record":{"id":"4fed44a28a3884b7","repo":"grafana/k6","slug":"disposing-browser-context-id-s-w","errorCode":null,"errorMessage":"disposing browser context ID %s: %w","messagePattern":"disposing browser context ID (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/js/modules/k6/browser/common/browser.go","lineNumber":174,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"browser connect: %w\", err)\n\t}\n\t// The connection's recvLoop reads defaultContext through\n\t// connectionOnAttachedToTarget, so publish it under contextMu.\n\tb.contextMu.Lock()\n\tb.defaultContext = defaultContext\n\tb.contextMu.Unlock()\n\tb.runOnClose = append(b.runOnClose, defaultContext.cleanup)\n\n\treturn b.initEvents()\n}\n\nfunc (b *Browser) disposeContext(id cdp.BrowserContextID) error {\n\tb.logger.Debugf(\"Browser:disposeContext\", \"bctxid:%v\", id)\n\n\taction := target.DisposeBrowserContext(id)\n\tif err := action.Do(cdp.WithExecutor(b.vuCtx, b.conn)); err != nil {\n\t\treturn fmt.Errorf(\"disposing browser context ID %s: %w\", id, err)\n\t}\n\tb.contextMu.Lock()\n\tb.context = nil\n\tb.contextMu.Unlock()\n\n\treturn nil\n}\n\n// getDefaultBrowserContextOrMatchedID returns the BrowserContext for the given browser context ID.\n// If the browser context is not found, the default BrowserContext is returned.\nfunc (b *Browser) getDefaultBrowserContextOrMatchedID(id cdp.BrowserContextID) *BrowserContext {\n\tb.contextMu.RLock()\n\tdefer b.contextMu.RUnlock()\n\n\tif b.context == nil || b.context.id != id {\n\t\treturn b.defaultContext\n\t}\n","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/browser.go#L156-L192","documentation":"BrowserContext.Close() sends Target.disposeBrowserContext over CDP to tear down the context in the browser. This error wraps the browser rejecting or failing that command. It is most often a teardown-ordering artifact: the context is already gone (double close, browser already closing/closed) or the CDP session itself is dead.","triggerScenarios":"Calling context.close() twice; calling context.close() after browser.close() started; closing a context while the WS connection is already lost (browser crashed); disposing a context id the browser no longer knows.","commonSituations":"finally blocks that close contexts while k6 teardown also closes the browser; double-close from test helpers; closing contexts during an aborted test run.","solutions":["Close each context exactly once, before closing the browser, and await the close","Track a closed flag per context and skip redundant close calls","Treat close errors during teardown as warnings: log and continue instead of failing the run","Check K6_BROWSER_LOG=debug for a preceding EventConnectionClose indicating the browser died first"],"exampleFix":"// before\nawait context.close();\nawait context.close(); // double close -> disposing browser context ID ...\n\n// after\nlet closed = false;\nasync function closeContext() {\n  if (closed) return;\n  closed = true;\n  await context.close();\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// During teardown, tolerate context disposal errors\ntry {\n  await context.close();\n} catch (e) {\n  if (/disposing browser context ID/.test(String(e))) {\n    console.warn('context already disposed or browser gone; ignoring');\n  } else throw e;\n}","preventionTips":["Close each context exactly once and before browser.close()","Track closed flags in helpers to prevent double close","Treat teardown close errors as warnings in reports"],"tags":["cdp","browser-context","cleanup","teardown"],"backgroundTag":"cdp-protocol-error","analyzedSha":"3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee","analyzedAt":"2026-09-20T22:01:45.163Z","contentChangedAt":"2026-09-20T22:01:45.163Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}