grafana/k6 · error

finding clickable point

Error message

finding clickable point: %w

What it means

clickablePoint() computes the center of the element from its bounding box before a pointer action; this error means BoundingBox() failed for the element. The wrapped cause is typically ErrElementNotVisible (box model not computable), ErrElementNotAttachedToDOM, or a CDP/session error.

Solutions

  1. Wait for the element to be actionable before clicking: await el.waitFor({ state: 'visible' }) or use locators, which auto-wait
  2. Scroll into view first with el.scrollIntoViewIfNeeded()
  3. If the element is animated, wait for it to finish (wait for opacity/size via el.waitFor() or a custom wait)
  4. Re-query the element fresh after any navigation instead of reusing an old handle

Example fix

// before
const el = page.locator('.hidden-tab >> text=Save');
await el.click();

// after
await page.locator('.tab-handle').click(); // reveal the tab first
const el = page.locator('.hidden-tab >> text=Save');
await el.waitFor({ state: 'visible' });
await el.click();
Defensive patterns

Strategy: validation

Validate before calling

if (!(await el.isVisible())) {
  await el.waitFor({ state: 'visible', timeout: 10_000 });
}
const box = await el.boundingBox();
if (!box || box.width === 0 || box.height === 0) {
  throw new Error('element has no clickable area');
}

Prevention

When it happens

Trigger: Calling click/dblclick/hover/check on an element with display:none or visibility:hidden, a zero-size element, an element detached from the DOM between resolution and action, or after the page/target has crashed or navigated away.

Common situations: Clicking buttons in hidden tabs, accordions, or carousel slides; elements behind 'display:none until animation completes'; acting on stale handles grabbed before a navigation; headless environments where fonts/layout differ and the element collapses to 0x0.

Related errors


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

Appendix: source

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

	case bool:
		return &v, nil
	}

	return nil, fmt.Errorf(
		"checking state %q of element %q", state, reflect.TypeOf(result))
}

func (h *ElementHandle) click(p *Position, opts *MouseClickOptions) error {
	return h.frame.page.Mouse.click(p.X, p.Y, opts)
}

// This will get the clickable point of the element relative to the page even
// when the element is in an iframe. In some cases this isn't the case and we
// need to translate the point to the page.
func (h *ElementHandle) clickablePoint() (*Position, error) {
	r, err := h.BoundingBox()
	if err != nil {
		return nil, fmt.Errorf("finding clickable point: %w", err)
	}
	return &Position{X: r.X + r.Width/2, Y: r.Y + r.Height/2}, nil
}

func (h *ElementHandle) dblclick(p *Position, opts *MouseClickOptions) error {
	return h.frame.page.Mouse.click(p.X, p.Y, opts)
}

// DefaultTimeout returns the default timeout for this element handle.
// If the receiver or any of the chained fields are nil (which can happen
// when the element handle is no longer attached to a live frame), the
// package-level DefaultTimeout constant is returned instead of panicking.
func (h *ElementHandle) DefaultTimeout() time.Duration {
	if h == nil || h.frame == nil || h.frame.manager == nil || h.frame.manager.timeoutSettings == nil {
		return DefaultTimeout
	}
	return h.frame.manager.timeoutSettings.timeout()
}

View on GitHub (pinned to 3fcf5388d7)