grafana/k6 · error
scrolling element into view: %w
Error message
scrolling element into view: %w
What it means
Before capturing an element, the screenshotter scrolls it into view and waits for it to be visible/stable (waitAndScrollIntoViewIfNeeded). This error wraps that wait failing — typically a timeout waiting for the element to become visible, the element becoming detached, or the execution context being destroyed by navigation.
Source
Thrown at internal/js/modules/k6/browser/common/screenshotter.go:281
"This is non-standard behavior, if possible please report this issue (with a reproducible script) "+
"to the https://github.com/grafana/k6/issues/new.",
visualViewportScale, visualViewportPageX, visualViewportPageY,
)
}
return visualViewportScale, visualViewportPageX, visualViewportPageY, nil
}
func (s *screenshotter) screenshotElement(h *ElementHandle, opts *ElementHandleScreenshotOptions) ([]byte, error) {
format := opts.Format
viewportSize, originalViewportSize, err := s.originalViewportSize(h.frame.page)
if err != nil {
return nil, fmt.Errorf("getting original viewport size: %w", err)
}
err = h.waitAndScrollIntoViewIfNeeded(h.ctx, false, true, opts.Timeout)
if err != nil {
return nil, fmt.Errorf("scrolling element into view: %w", err)
}
bbox, err := h.boundingBox()
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")
}
var overriddenViewportSize *Size
fitsViewport := bbox.Width <= viewportSize.Width && bbox.Height <= viewportSize.Height
if !fitsViewport { //nolint:nestif
overriddenViewportSize = Size{
Width: math.Max(viewportSize.Width, bbox.Width),View on GitHub (pinned to 93accf6570)
Solutions
- Wait first: await locator.waitFor({ state: 'visible', timeout: 30000 }) before screenshot() (or pass a larger timeout option to screenshot).
- Expand/scroll the container in user code so the element is actually visible.
- Re-query the element close to the screenshot to avoid stale handles in SPAs.
- If the page navigates on a timer, block that navigation or screenshot sooner.
Example fix
// before
await page.$('#chart').then(el => el.screenshot({ path: 'c.png' })); // chart not rendered yet
// after
const chart = page.locator('#chart');
await chart.waitFor({ state: 'visible', timeout: 30000 });
await chart.screenshot({ path: 'c.png' }); Defensive patterns
Strategy: validation
Validate before calling
const loc = page.locator('#target');
await loc.waitFor({ state: 'visible', timeout: 30000 }); Type guard
async function isVisibleAndAttached(loc) {
return (await loc.count()) > 0 && await loc.isVisible();
} Try / catch
try { await el.screenshot({ path: 'e.png' }); }
catch (e) { if (String(e).includes('scrolling element into view')) { /* wait for visible, then retry */ } else throw e; } Prevention
- waitFor({state:'visible'}) before every element screenshot.
- Pass a generous timeout option on slow CI.
- Expand collapsed containers before capturing their content.
When it happens
Trigger: elementHandle.screenshot() where the element is display:none/visibility:hidden/offscreen-unscrollable, is removed from the DOM during the wait, the selector resolved to a stale handle, or the page navigates while waiting.
Common situations: Screenshotting lazy-rendered widgets (charts, carousels) before they render; elements inside collapsed accordions/tabs; SPA route changes invalidating the handle; default timeout too short on slow CI.
Related errors
- node is either not visible or not an HTMLElement: %w
- parsing element handle screenshot options: %w
- getting original viewport size: %w
- node has 0 width
- node has 0 height
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/1d35c34873d04e74.
Report an issue: GitHub.