grafana/k6 · warning

internal error while removing binding from page: %w

Error message

internal error while removing binding from page: %w

What it means

During Page.Close, k6 attempts to remove the internal web-vitals binding from the page via runtime.RemoveBinding before closing the target. This error is recorded (the close still proceeds) when that CDP call fails, typically because the page crashed, already navigated away destroying the binding world, or the teardown timeout context expired. It is joined with other close errors and surfaced from page.close().

Source

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

			Object.defineProperty(document, 'visibilityState', {value: 'hidden', configurable: true});
			Object.defineProperty(document, 'hidden', {value: true, configurable: true});
			document.dispatchEvent(new Event('visibilitychange'));
		}`
		if _, err := p.MainFrame().EvaluateWithContext(teardownTimeoutCtx, v); err != nil {
			p.logger.Warnf("Page:Close", "failed to trigger visibilitychange for web vitals: %v", err)
		}

		keyDown := input.DispatchKeyEvent(input.KeyDown).WithKey("Escape")
		if err := keyDown.Do(cdp.WithExecutor(teardownTimeoutCtx, p.session)); err != nil {
			p.logger.Warnf("Page:Close", "failed to dispatch keydown for web vitals: %v", err)
		}

		var closeErrs []error

		add := runtime.RemoveBinding(webVitalBinding)
		if err := add.Do(cdp.WithExecutor(teardownTimeoutCtx, p.session)); err != nil {
			// continue so that we can shutdown the page even if we fail to remove the binding.
			closeErrs = append(closeErrs, fmt.Errorf("internal error while removing binding from page: %w", err))
		}

		err := target.CloseTarget(p.targetID).Do(cdp.WithExecutor(teardownTimeoutCtx, p.session))
		if err != nil && !errors.Is(err, context.Canceled) {
			// When a close target command is sent to the browser via CDP,
			// the browser will start to cleanup and the first thing it
			// will do is return a target.EventDetachedFromTarget, which in
			// our implementation will close the session connection (this
			// does not close the CDP websocket, just removes the session
			// so no other CDP calls can be made with the session ID).
			// This can result in the session's context being closed while
			// we're waiting for the response to come back from the browser
			// for this current command (it's racey).
			closeErrs = append(closeErrs, fmt.Errorf("closing a page: %w", err))
		}

		// Start the teardown of the page's resources (FrameSessions, NetworkManagers, etc)
		// and wait for them to finish their teardown. This allows for a graceful cleanup

View on GitHub (pinned to 93accf6570)

Solutions

  1. Close pages before closing the browser or ending the iteration (use a try/finally block)
  2. Increase the browserContext timeout option if teardown is being cut short
  3. Investigate renderer crashes (browser logs, --disable-dev-shm-usage in a Docker host) if a page is dying before close
  4. Treat as non-fatal if the page close otherwise succeeded and no further operations use that page

Example fix

// before
// page left open, iteration ends, teardown races

// after
const page = await context.newPage();
try {
  await page.goto('https://example.com/');
} finally {
  await page.close().catch(e => console.warn('close warning:', e.message));
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Try / catch

try {
  await page.close();
} catch (e) {
  if (/removing binding from page/.test(e.message)) { /* non-fatal teardown noise */ }
  else throw e;
}

Prevention

When it happens

Trigger: page.close() on a page whose renderer already crashed (OOM, segfault), a page mid-navigation when the teardown timeout (browserContext timeout) fires, or a page whose target the browser already destroyed.

Common situations: Heavy pages that OOM the renderer; very short timeouts on the browser context causing teardown to race; closing pages after browser.close() was already initiated; 'page crashed' events earlier in the log.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/e900b0e74a00b938. Report an issue: GitHub.