{"record":{"id":"5db87485e3a49b76","repo":"grafana/k6","slug":"disposing-browser-context-w","errorCode":null,"errorMessage":"disposing browser context: %w","messagePattern":"disposing browser context: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/browser_context.go","lineNumber":213,"sourceCode":"\tb.logger.Debugf(\"BrowserContext:ClearPermissions\", \"bctxid:%v\", b.id)\n\n\taction := cdpbrowser.ResetPermissions().WithBrowserContextID(b.id)\n\tif err := action.Do(cdp.WithExecutor(b.ctx, b.browser.conn)); err != nil {\n\t\treturn fmt.Errorf(\"clearing permissions: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// Close shuts down the browser context.\nfunc (b *BrowserContext) Close() error {\n\tb.logger.Debugf(\"BrowserContext:Close\", \"bctxid:%v\", b.id)\n\n\tif b.id == \"\" {\n\t\treturn fmt.Errorf(\"default browser context can't be closed\")\n\t}\n\tif err := b.browser.disposeContext(b.id); err != nil {\n\t\treturn fmt.Errorf(\"disposing browser context: %w\", err)\n\t}\n\treturn nil\n}\n\n// GrantPermissions enables the specified permissions, all others will be disabled.\nfunc (b *BrowserContext) GrantPermissions(permissions []string, opts GrantPermissionsOptions) error {\n\tb.logger.Debugf(\"BrowserContext:GrantPermissions\", \"bctxid:%v\", b.id)\n\n\tpermsToProtocol := map[string]cdpbrowser.PermissionType{\n\t\t\"geolocation\":          cdpbrowser.PermissionTypeGeolocation,\n\t\t\"midi\":                 cdpbrowser.PermissionTypeMidi,\n\t\t\"midi-sysex\":           cdpbrowser.PermissionTypeMidiSysex,\n\t\t\"notifications\":        cdpbrowser.PermissionTypeNotifications,\n\t\t\"camera\":               cdpbrowser.PermissionTypeVideoCapture,\n\t\t\"microphone\":           cdpbrowser.PermissionTypeAudioCapture,\n\t\t\"background-sync\":      cdpbrowser.PermissionTypeBackgroundSync,\n\t\t\"ambient-light-sensor\": cdpbrowser.PermissionTypeSensors,\n\t\t\"accelerometer\":        cdpbrowser.PermissionTypeSensors,","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/js/modules/k6/browser/common/browser_context.go#L195-L231","documentation":"Close() passed the guard for the default context and issued Target.disposeBrowserContext via browser.disposeContext(b.id), but the CDP call failed. The wrapped error usually says the browser context ID was not found (already disposed) or reports a lost connection. The context object on the Go side may be left in a half-closed state.","triggerScenarios":"Closing a context twice (double-close); closing a context the browser already disposed of during shutdown; closing while the CDP WebSocket to Chromium is disconnected; pages in the context being torn down concurrently.","commonSituations":"User teardown code and k6's own cleanup both closing contexts; close() in a finally block running after the browser process died; races between parallel VU iterations closing a shared context.","solutions":["Close each context exactly once — track it with a flag or rely on structure instead of repeated finally blocks","Order teardown: close pages, then contexts, then the browser, and don't close contexts after browser.close()","Inspect the wrapped error: 'not found' means it was already gone (safe to ignore), connection errors mean the browser died","Wrap teardown closes in try/catch and log rather than fail the test"],"exampleFix":"// before\nctx.close();\n// ... later, cleanup path runs again\nctx.close(); // throws 'disposing browser context'\n\n// after\nlet closed = false;\nfunction cleanup() {\n  if (!closed) {\n    closed = true;\n    ctx.close();\n  }\n}","handlingStrategy":"try-catch","validationCode":"// make close idempotent in your page/context wrapper\nfunction once(fn) {\n  let done = false;\n  return () => {\n    if (!done) { done = true; fn(); }\n  };\n}\nconst closeCtx = once(() => ctx.close());","typeGuard":null,"tryCatchPattern":"try {\n  ctx.close();\n} catch (e) {\n  const msg = String(e.message);\n  if (msg.includes('disposing browser context') && /not found|no target/i.test(msg)) {\n    // already disposed elsewhere: safe to ignore\n  } else {\n    throw e;\n  }\n}","preventionTips":["Close each context exactly once; guard with a closed flag","Order teardown: pages -> contexts -> browser, and never close contexts after the browser","Treat 'context id not found' wrapped errors as benign in cleanup paths"],"tags":["browser","cdp","browser-context","lifecycle"],"backgroundTag":"cdp-protocol-error","analyzedSha":"01ffac6f245854c1b8adc6a69857c76a15f90022","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}