{"record":{"id":"e900b0e74a00b938","repo":"grafana/k6","slug":"internal-error-while-removing-binding-from-page","errorCode":null,"errorMessage":"internal error while removing binding from page: %w","messagePattern":"internal error while removing binding from page: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/js/modules/k6/browser/common/page.go","lineNumber":1007,"sourceCode":"\t\t\tObject.defineProperty(document, 'visibilityState', {value: 'hidden', configurable: true});\n\t\t\tObject.defineProperty(document, 'hidden', {value: true, configurable: true});\n\t\t\tdocument.dispatchEvent(new Event('visibilitychange'));\n\t\t}`\n\t\tif _, err := p.MainFrame().EvaluateWithContext(teardownTimeoutCtx, v); err != nil {\n\t\t\tp.logger.Warnf(\"Page:Close\", \"failed to trigger visibilitychange for web vitals: %v\", err)\n\t\t}\n\n\t\tkeyDown := input.DispatchKeyEvent(input.KeyDown).WithKey(\"Escape\")\n\t\tif err := keyDown.Do(cdp.WithExecutor(teardownTimeoutCtx, p.session)); err != nil {\n\t\t\tp.logger.Warnf(\"Page:Close\", \"failed to dispatch keydown for web vitals: %v\", err)\n\t\t}\n\n\t\tvar closeErrs []error\n\n\t\tadd := runtime.RemoveBinding(webVitalBinding)\n\t\tif err := add.Do(cdp.WithExecutor(teardownTimeoutCtx, p.session)); err != nil {\n\t\t\t// continue so that we can shutdown the page even if we fail to remove the binding.\n\t\t\tcloseErrs = append(closeErrs, fmt.Errorf(\"internal error while removing binding from page: %w\", err))\n\t\t}\n\n\t\terr := target.CloseTarget(p.targetID).Do(cdp.WithExecutor(teardownTimeoutCtx, p.session))\n\t\tif err != nil && !errors.Is(err, context.Canceled) {\n\t\t\t// When a close target command is sent to the browser via CDP,\n\t\t\t// the browser will start to cleanup and the first thing it\n\t\t\t// will do is return a target.EventDetachedFromTarget, which in\n\t\t\t// our implementation will close the session connection (this\n\t\t\t// does not close the CDP websocket, just removes the session\n\t\t\t// so no other CDP calls can be made with the session ID).\n\t\t\t// This can result in the session's context being closed while\n\t\t\t// we're waiting for the response to come back from the browser\n\t\t\t// for this current command (it's racey).\n\t\t\tcloseErrs = append(closeErrs, fmt.Errorf(\"closing a page: %w\", err))\n\t\t}\n\n\t\t// Start the teardown of the page's resources (FrameSessions, NetworkManagers, etc)\n\t\t// and wait for them to finish their teardown. This allows for a graceful cleanup","sourceCodeStart":989,"sourceCodeEnd":1025,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/page.go#L989-L1025","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","solutions":["Close pages before closing the browser or ending the iteration (use a try/finally block)","Increase the browserContext timeout option if teardown is being cut short","Investigate renderer crashes (browser logs, --disable-dev-shm-usage in a Docker host) if a page is dying before close","Treat as non-fatal if the page close otherwise succeeded and no further operations use that page"],"exampleFix":"// before\n// page left open, iteration ends, teardown races\n\n// after\nconst page = await context.newPage();\ntry {\n  await page.goto('https://example.com/');\n} finally {\n  await page.close().catch(e => console.warn('close warning:', e.message));\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"try {\n  await page.close();\n} catch (e) {\n  if (/removing binding from page/.test(e.message)) { /* non-fatal teardown noise */ }\n  else throw e;\n}","preventionTips":["Always close pages explicitly before the browser or iteration ends (try/finally)","Set a generous browserContext timeout so teardown is not cut short","Watch for earlier renderer-crash events; fix those to avoid close-time binding failures"],"tags":["browser","page-close","teardown","cdp","web-vitals"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}