grafana/k6 · error
disposing browser context: %w
Error message
disposing browser context: %w
What it means
BrowserContext.close() calls Target.disposeBrowserContext over CDP via browser.disposeContext (browser_context.go:213); a transport or protocol failure is wrapped as 'disposing browser context: %w'. Typical underlying causes: the context was already disposed (double close), the browser is closing, or the connection is lost.
Source
Thrown at internal/js/modules/k6/browser/common/browser_context.go:213
b.logger.Debugf("BrowserContext:ClearPermissions", "bctxid:%v", b.id)
action := cdpbrowser.ResetPermissions().WithBrowserContextID(b.id)
if err := action.Do(cdp.WithExecutor(b.ctx, b.browser.conn)); err != nil {
return fmt.Errorf("clearing permissions: %w", err)
}
return nil
}
// Close shuts down the browser context.
func (b *BrowserContext) Close() error {
b.logger.Debugf("BrowserContext:Close", "bctxid:%v", b.id)
if b.id == "" {
return fmt.Errorf("default browser context can't be closed")
}
if err := b.browser.disposeContext(b.id); err != nil {
return fmt.Errorf("disposing browser context: %w", err)
}
return nil
}
// GrantPermissions enables the specified permissions, all others will be disabled.
func (b *BrowserContext) GrantPermissions(permissions []string, opts GrantPermissionsOptions) error {
b.logger.Debugf("BrowserContext:GrantPermissions", "bctxid:%v", b.id)
permsToProtocol := map[string]cdpbrowser.PermissionType{
"geolocation": cdpbrowser.PermissionTypeGeolocation,
"midi": cdpbrowser.PermissionTypeMidi,
"midi-sysex": cdpbrowser.PermissionTypeMidiSysex,
"notifications": cdpbrowser.PermissionTypeNotifications,
"camera": cdpbrowser.PermissionTypeVideoCapture,
"microphone": cdpbrowser.PermissionTypeAudioCapture,
"background-sync": cdpbrowser.PermissionTypeBackgroundSync,
"ambient-light-sensor": cdpbrowser.PermissionTypeSensors,
"accelerometer": cdpbrowser.PermissionTypeSensors,View on GitHub (pinned to 93accf6570)
Solutions
- Close each context exactly once; null the reference after close()
- Ensure context.close() calls all happen before browser.close() in teardown
- Read the wrapped error to distinguish double-dispose from dead connection
- Use an idempotent close helper that treats 'already disposed' as success
Example fix
// before
await ctx.close()
await ctx.close() // second call may throw: disposing browser context
// after
let open = true
async function closeCtx() { if (open) { await ctx.close(); open = false } }
await closeCtx()
await closeCtx() // no-op Defensive patterns
Strategy: try-catch
Validate before calling
let ctxOpen = true
async function closeContext() {
if (!ctxOpen) return
await ctx.close()
ctxOpen = false
} Try / catch
try {
await ctx.close()
} catch (e) {
if (/disposing browser context/.test(String(e)) && /invalid browserContextId|already|closed/i.test(String(e))) {
ctxOpen = false // treat double-dispose as success
} else throw e
} Prevention
- Close each context exactly once; null the reference afterwards
- Order teardown: context.close() before browser.close()
- Use an idempotent close helper in shared code
When it happens
Trigger: Calling close() twice on the same context; closing a context after browser.close(); the connection dropping mid-close; two VUs closing the same shared context concurrently.
Common situations: Double close from both a helper and teardown; stale context references kept in module-level globals; chromium crashing before teardown runs.
Related errors
- clearing cookies: %w
- retrieving cookies: %w
- adding k6 object to new browser context: %w
- browser connect: %w
- disposing browser context ID %s: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/5db87485e3a49b76.
Report an issue: GitHub.