{"record":{"id":"9dfb7c27ca2afc34","repo":"grafana/k6","slug":"evaluating-scroll-offset-w","errorCode":null,"errorMessage":"evaluating scroll offset: %w","messagePattern":"evaluating scroll offset: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/screenshotter.go","lineNumber":324,"sourceCode":"\t\terr = h.waitAndScrollIntoViewIfNeeded(h.ctx, false, true, opts.Timeout)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"scrolling element into view: %w\", err)\n\t\t}\n\t\tbbox, err = h.boundingBox()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"node is either not visible or not an HTMLElement: %w\", err)\n\t\t}\n\t\tif bbox.Width <= 0 {\n\t\t\treturn nil, fmt.Errorf(\"node has 0 width\")\n\t\t}\n\t\tif bbox.Height <= 0 {\n\t\t\treturn nil, fmt.Errorf(\"node has 0 height\")\n\t\t}\n\t}\n\n\tscrollOffset, err := h.Evaluate(`() => { return {x: window.scrollX, y: window.scrollY};}`)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"evaluating scroll offset: %w\", err)\n\t}\n\n\tvar returnVal Position\n\tif err := convert(scrollOffset, &returnVal); err != nil {\n\t\treturn nil, fmt.Errorf(\"unpacking scroll offset: %w\", err)\n\t}\n\n\tdocumentRect := bbox\n\tdocumentRect.X += returnVal.X\n\tdocumentRect.Y += returnVal.Y\n\n\tbuf, err := s.screenshot(\n\t\th.frame.page.session,\n\t\tdocumentRect.enclosingIntRect(),\n\t\tnil, // viewportRect\n\t\tformat,\n\t\topts.OmitBackground,\n\t\topts.Quality,","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/screenshotter.go#L306-L342","documentation":"Near the end of elementHandle.screenshot(), the code evaluates `() => ({x: window.scrollX, y: window.scrollY})` on the element to translate the viewport-relative bounding box into document coordinates for the CDP clip. This evaluate call failed (wrapped verbatim).","triggerScenarios":"Execution context destroyed by a navigation or DOM-ready reload between boundingBox and evaluate; page/target closed; renderer crash; k6 context cancelled (timeout/teardown) mid-screenshot.","commonSituations":"Pages with auto-refresh/meta-refresh or SPA route changes during capture; screenshot racing page.close(); slow pages where the element screenshot exceeds the browser-timeout budget.","solutions":["Suppress navigation during capture (block redirects, avoid auto-refreshing pages) or capture immediately after load settles.","Await everything before screenshot and keep the page open until it resolves.","Increase the browser timeout option if the whole flow is just slow.","Retry once — transient context-destroyed errors are common."],"exampleFix":"// before\nawait page.$('#report').then(el => el.screenshot({ path: 'r.png' })); // page auto-refreshes mid-capture\n\n// after\nawait page.route('**/refresh*', r => r.abort()); // or wait past the refresh\nawait page.goto(url); await page.waitForLoadState('load');\nawait (await page.$('#report')).screenshot({ path: 'r.png' });","handlingStrategy":"try-catch","validationCode":"await page.waitForLoadState('load'); // and block auto-refresh: await page.route('**/refresh*', r => r.abort());","typeGuard":null,"tryCatchPattern":"try { await el.screenshot({ path: 'e.png' }); }\ncatch (e) {\n  if (/evaluating scroll offset|Execution context was destroyed/i.test(String(e))) {\n    await sleep(500); await el.screenshot({ path: 'e.png' }); // one retry\n  } else throw e;\n}","preventionTips":["Do not screenshot pages that auto-navigate during capture.","Keep the page open and idle through the whole call.","Budget enough browser timeout for element screenshots on heavy pages."],"tags":["k6","browser","screenshot","element-handle","execution-context","navigation-race"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}