grafana/k6 · error

node has 0 width

Error message

node has 0 width

What it means

Hard guard in element screenshots: after computing the element's bounding box, its width is <= 0, so there is nothing to capture. The element exists and has a box, but it renders with zero width (height may be fine). Zero-size captures also crash some Chromium versions, hence the explicit check.

Solutions

  1. Verify computed width in DevTools; fix the CSS (min-width) or wait for content to expand the element.
  2. Wait for images/fonts: await page.waitForLoadState('networkidle') or wait for a size-bearing child.
  3. If capturing a visual, screenshot the parent container that has non-zero size instead.
  4. Set an explicit width on the element in test setup.

Example fix

// before
await page.$('.icon-host').screenshot({ path: 'i.png' }); // host is 0px wide

// after
await page.$('.icon-wrapper').screenshot({ path: 'i.png' }); // sized container
// or CSS: .icon-host { min-width: 32px; }
Defensive patterns

Strategy: validation

Validate before calling

const box = await handle.boundingBox();
if (!box || box.width <= 0) throw new Error(`element width is ${box ? box.width : 'unknown'}; not capturable`);

Type guard

const hasNonZeroWidth = async (h) => {
  const b = await h.boundingBox();
  return !!b && b.width > 0;
};

Prevention

When it happens

Trigger: await elementHandle.screenshot() on elements styled with width: 0 (or content collapsed by CSS: absolute positioning off-viewport, flex/grid item shrunk to 0, whitespace stripped), or an element whose content has not laid out yet (fonts/images not loaded).

Common situations: Screenshotting skeleton loaders or icons rendered via ::before only (host has 0 width); collapsed flex children; elements measured before web fonts/images expand them; responsive layouts under a narrow emulated viewport.

Related errors


AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18). Data as JSON: /api/errors/100e946c6efaf963. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/common/screenshotter.go:307

) (buf []byte, err error) {
	format := opts.Format
	viewportSize, err := s.originalViewportSize(h.frame.page)
	if err != nil {
		return nil, fmt.Errorf("getting original viewport size: %w", err)
	}
	originalSize := h.frame.page.emulatedSize

	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")
	}

	fitsViewport := bbox.Width <= viewportSize.Width && bbox.Height <= viewportSize.Height
	if !fitsViewport { //nolint:nestif
		overriddenViewportSize := Size{
			Width:  math.Max(viewportSize.Width, bbox.Width),
			Height: math.Max(viewportSize.Height, bbox.Height),
		}.enclosingIntSize()
		defer func() {
			if restoreErr := s.restoreViewport(h.frame.page, originalSize); restoreErr != nil {
				err = errors.Join(err, fmt.Errorf("restoring viewport: %w", restoreErr))
			}
		}()
		if err := h.frame.page.setViewportSize(s.ctx, overriddenViewportSize); err != nil {
			return nil, fmt.Errorf("setting viewport size to %s: %w",

View on GitHub (pinned to 3fcf5388d7)