grafana/k6 · error
unpacking scroll offset: %w
Error message
unpacking scroll offset: %w
What it means
The final conversion in elementHandle.screenshot(): the evaluated scroll offset {x: window.scrollX, y: window.scrollY} is converted into a Position struct via the JSON round-trip helper, and that conversion failed — the evaluated value did not deserialize into {x,y} numeric fields.
Source
Thrown at internal/js/modules/k6/browser/common/screenshotter.go:329
if err != nil {
return nil, fmt.Errorf("node is either not visible or not an HTMLElement: %w", err)
}
if bbox.Width <= 0 {
return nil, fmt.Errorf("node has 0 width")
}
if bbox.Height <= 0 {
return nil, fmt.Errorf("node has 0 height")
}
}
scrollOffset, err := h.Evaluate(`() => { return {x: window.scrollX, y: window.scrollY};}`)
if err != nil {
return nil, fmt.Errorf("evaluating scroll offset: %w", err)
}
var returnVal Position
if err := convert(scrollOffset, &returnVal); err != nil {
return nil, fmt.Errorf("unpacking scroll offset: %w", err)
}
documentRect := bbox
documentRect.X += returnVal.X
documentRect.Y += returnVal.Y
buf, err := s.screenshot(
h.frame.page.session,
documentRect.enclosingIntRect(),
nil, // viewportRect
format,
opts.OmitBackground,
opts.Quality,
opts.Path,
)
if err != nil {
return nil, err
}View on GitHub (pinned to 93accf6570)
Solutions
- Screenshot with page.screenshot({clip: boundingBox}) instead of element screenshot to bypass the scroll-offset math.
- If you control the page, remove scripts that override window.scrollX/scrollY.
- Scroll to top first (page.evaluate('window.scrollTo(0,0)')) so offsets are plain zeros.
- Upgrade k6 and report a grafana/k6 issue if it reproduces on a vanilla page.
Example fix
// before
await page.$('#el').then(el => el.screenshot({ path: 'e.png' })); // scrollX patched to a string
// after
await page.evaluate('window.scrollTo(0, 0)');
const bb = await page.locator('#el').boundingBox();
await page.screenshot({ path: 'e.png', clip: bb }); Defensive patterns
Strategy: try-catch
Validate before calling
await page.evaluate('window.scrollTo(0, 0)'); // make scroll offsets plain zeros before element capture Type guard
function isFinitePosition(v) {
return v != null && typeof v === 'object'
&& Number.isFinite(Number(v.x)) && Number.isFinite(Number(v.y));
} Try / catch
try { await el.screenshot({ path: 'e.png' }); }
catch (e) {
if (String(e).includes('unpacking scroll offset')) {
const bb = await page.locator(sel).boundingBox();
await page.screenshot({ path: 'e.png', clip: bb }); // bypass scroll-offset math
} else throw e;
} Prevention
- Scroll to top before element screenshots.
- Avoid pages that monkey-patch window.scrollX/scrollY.
- Fall back to page.screenshot({clip}) when element capture keeps failing.
When it happens
Trigger: The scrollOffset evaluate succeeded but returned a non-numeric/oddly-shaped value (a string, undefined, or an object whose x/y are not numbers — e.g. a page script overriding window.scrollX/scrollY with non-numeric values).
Common situations: Anti-automation or analytics scripts monkey-patching window.scrollX/scrollY; unusual document types; extremely rare in normal pages since these are built-in number properties.
Related errors
- parsing element handle screenshot options: %w
- parsing scrollIntoViewIfNeeded options: %w
- converting result (%v of type %t) to size: %w
- unpacking window size: %w
- getting original viewport size: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/c5f29661924745ab.
Report an issue: GitHub.