grafana/k6 · error

pointer action

Error message

pointer action: %w

What it means

Stage of the k6 browser module's pointer-action pipeline that resolves the element's clickable point when no custom position was given: clickablePoint() reads the element's BoundingBox and takes its center. This error (labelled 'pointer action') wraps that failing — the inner error is 'finding clickable point', caused by a missing bounding box (hidden element, zero-size element, detached node, or element inside an unrendered iframe).

Solutions

  1. Wait until the element has actual size: waitForSelector(sel, { state: 'visible' }) plus (if needed) a boundingBox retry loop checking width/height > 0.
  2. Await font/image load: page.waitForLoadState('networkidle') before acting.
  3. Re-query the handle right before the click to avoid detachment races.
  4. If the element is intentionally zero-size, click a positioned parent or use { position } on an ancestor with a real box.

Example fix

// before
const el = await page.$('#icon-button');
await el.click(); // zero-size until icon font loads

// after
await page.waitForLoadState('networkidle');
const el = await page.waitForSelector('#icon-button', { state: 'visible' });
await el.click();
Defensive patterns

Strategy: validation

Validate before calling

const box = await el.boundingBox();
if (!box || box.width <= 0 || box.height <= 0) {
  throw new Error(`element has no clickable box (${JSON.stringify(box)})`);
}
await el.click();

Type guard

async function isClickable(el) {
  const b = await el.boundingBox().catch(() => null);
  return !!b && b.width > 0 && b.height > 0;
}

Try / catch

try {
  await el.click();
} catch (e) {
  if (String(e).includes('pointer action')) {
    await page.waitForLoadState('networkidle');
    el = await page.waitForSelector(sel, { state: 'visible' });
    await el.click();
  } else { throw e; }
}

Prevention

When it happens

Trigger: click()/dblclick()/hover() without position on an element with no rendered box: display:none, width/height 0 (e.g., an empty inline element), an <svg> or custom element not yet upgraded, or a node detached after passing earlier checks.

Common situations: Clicking placeholder elements before content renders; zero-size anchors used as JS hooks; elements that only get size after fonts/images load (CLS); virtualized list rows unmounted mid-action.

Related errors


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

Appendix: source

Thrown at internal/js/modules/k6/browser/common/element_handle.go:1751

				evalOptions{forceCallable: true, returnByValue: false},
				js.ScrollIntoView,
				sopts,
			)
		}
		if err != nil {
			return nil, fmt.Errorf("scrolling element into view: %w", err)
		}

		// Get the clickable point
		if p != nil {
			p, err = h.offsetPosition(apiCtx, opts.Position)
			if err != nil {
				return nil, fmt.Errorf("getting element position: %w", err)
			}
		} else {
			p, err = h.clickablePoint()
			if err != nil {
				return nil, fmt.Errorf("pointer action: %w", err)
			}
		}

		// Further translation of the point might be necessary if the point
		// is still relative to the parent frame it is in and not the page.
		*p, err = h.translatePointToPage(apiCtx, *p)
		if err != nil {
			return nil, fmt.Errorf("translating point to page: %w", err)
		}

		// Do a final actionability check to see if element can receive events
		// at mouse position in question
		if !opts.Force {
			if ok, err := h.checkHitTargetAt(apiCtx, *p); !ok {
				return nil, fmt.Errorf("checking hit target: %w", err)
			}
		}
		// Are we only "trialing" the action but not actually performing

View on GitHub (pinned to 3fcf5388d7)