grafana/k6 · error · errBrowserClosing

browser is closing

Error message

browser is closing

What it means

The Browser type keeps an atomic closing flag set by Close() and by context teardown. When a new target (page/popup) tries to attach after closing has started, attachNewPage rejects it with errBrowserClosing (internal/js/modules/k6/browser/common/browser.go:368). The guard prevents mutating a browser that is already shutting down; the error usually surfaces as a race between teardown and a late CDP Target.attachedToTarget event.

Source

Thrown at internal/js/modules/k6/browser/common/browser.go:369

		}

		detachSession(session)

		return nil
	}

	// Emit the page event only for pages, not for background pages.
	// Background pages are created by extensions.
	if isPage {
		browserCtx.emit(EventBrowserContextPage, p)
	}

	return nil
}

// errBrowserClosing is returned when a page attachment
// is rejected because the browser has started closing.
var errBrowserClosing = errors.New("browser is closing")

// attachNewPage checks whether the browser is closing and, if not, attaches
// the page. Returns errBrowserClosing if the browser is shutting down.
func (b *Browser) attachNewPage(p *Page, ev *target.EventAttachedToTarget) error {
	targetPage := ev.TargetInfo

	attachPage := func() error {
		b.pagesMu.Lock()
		defer b.pagesMu.Unlock()

		if b.closing.Load() {
			return errBrowserClosing
		}

		b.logger.Debugf(
			"Browser:attachNewPage:addTarget",
			"sid:%v tid:%v pageType:%s",
			ev.SessionID, targetPage.TargetID, targetPage.Type,

View on GitHub (pinned to 94169daab2)

Solutions

  1. Await every action that can open a page before closing: const popup = await page.waitForEvent('popup'); await popup.close(); then browser.close() last
  2. If a popup may appear, handle it explicitly (waitForEvent('popup')) instead of letting it race teardown
  3. During teardown, catch and ignore this specific error when the page is no longer needed
  4. Extend the scenario/iteration timeout if k6 is canceling mid-navigation

Example fix

// before
await page.click('#opens-popup');
browser.close(); // popup attach still in flight -> "browser is closing"

// after
const popup = await page.waitForEvent('popup');
await popup.close();
browser.close();
Defensive patterns

Strategy: try-catch

Try / catch

// tolerate the teardown race when the extra page is no longer needed
try {
  const page = await browser.newPage();
} catch (e) {
  if (!String(e?.message || e).includes('browser is closing')) throw e;
  // shutting down — skip page setup
}

Prevention

When it happens

Trigger: A click/navigation opens a popup at the same moment browser.close() runs at the end of the default function; calling browser.newPage()/newContext() after close(); the scenario timeout canceling the context while a target-attached event is in flight; reusing a browser handle across iterations after it was closed.

Common situations: Popup-heavy tests where the last interaction triggers a new tab during teardown; close() in a finally block racing in-flight CDP events; shared browser instances across VUs; tight test durations that cut off page creation.

Related errors


AI-assisted analysis of grafana/k6@94169daab2 (2026-08-18). Data as JSON: /api/errors/8539394ca6d0721a. Report an issue: GitHub.