grafana/k6 · error

node has 0 height

Error message

node has 0 height

What it means

Sibling of the zero-width guard: the element's bounding box height is <= 0 at screenshot time, so the capture is refused. Width may be positive, but a zero-height element (collapsed row, empty block before content loads) yields no pixels.

Solutions

  1. Wait for the content that gives the element height (chart rendered, items loaded) before capturing.
  2. Give the container a min-height in test CSS or app CSS.
  3. Re-check with a computed-style assertion in page.evaluate(() => getComputedStyle(el).height).
  4. Screenshot a parent wrapper with non-zero height instead.

Example fix

// before
const c = page.$('#chart');
await c.waitFor(); // attached but 0px tall
await c.screenshot({ path: 'c.png' });

// after
const c = page.$('#chart');
await c.waitFor();
await page.waitForFunction(() => document.querySelector('#chart').clientHeight > 0);
await c.screenshot({ path: 'c.png' });
Defensive patterns

Strategy: validation

Validate before calling

await page.waitForFunction((sel) =>
  document.querySelector(sel)?.clientHeight > 0, selector);
await handle.screenshot({ path: 'e.png' });

Type guard

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

Prevention

When it happens

Trigger: await elementHandle.screenshot() on elements with height: 0 / collapsed (empty container before async content renders, display collapsed by CSS grid auto rows, element whose only content is absolutely positioned and removed from flow).

Common situations: Screenshotting a chart container before the chart library initializes it; empty list/table bodies; accordions measured while closed; elements whose height comes only from floated/absolute children that have not rendered.

Related errors


AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-09-20). Data as JSON: /api/errors/df64217bbc30724e. Report an issue: GitHub.

Appendix: source

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

	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",
				overriddenViewportSize, err)
		}
		err = h.waitAndScrollIntoViewIfNeeded(h.ctx, false, true, opts.Timeout)

View on GitHub (pinned to 3fcf5388d7)