grafana/k6 · error

finding clickable point: %w

Error message

finding clickable point: %w

What it means

ElementHandle.clickablePoint() computes the center of an element for pointer actions (click, dblclick, hover) by calling BoundingBox(). This error wraps a BoundingBox failure: most often 'element is not visible: ... Could not compute box model', because chromium cannot produce a box model for an element that is hidden, has no layout object, or is detached from the DOM.

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 93accf6570)

Solutions

  1. Re-locate the element immediately before the action instead of reusing an old handle
  2. Wait for visibility first: await page.waitForSelector(sel, { state: 'visible' }) or use a locator with auto-waiting
  3. Raise the action timeout option and/or browser.set_default_timeout for slow pages
  4. If the element is intentionally hidden and must still be clicked, pass { force: true } to skip actionability checks
  5. Inspect the wrapped error text: if it mentions 'target closed' or CDP failures, the page/session died rather than the element being invisible

Example fix

// before
const btn = await page.$('#submit');
await sleep(5000); // page re-renders, node replaced
await btn.click(); // finding clickable point: element is not visible

// after
await page.waitForSelector('#submit', { state: 'visible' });
const btn = await page.$('#submit');
await btn.click();
Defensive patterns

Strategy: validation

Validate before calling

// Verify geometry exists before any pointer action
const box = await handle.boundingBox();
if (!box || box.width === 0 || box.height === 0) {
  await page.waitForSelector(sel, { state: 'visible' });
}
await handle.click();

Try / catch

try {
  await handle.click();
} catch (e) {
  if (String(e).includes('element is not visible')) {
    // re-locate and retry once with a fresh handle
    await (await page.waitForSelector(sel, { state: 'visible' })).click();
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling click()/dblclick()/hover() on an element with display:none or visibility:hidden, an element with zero width/height, a handle whose node was removed from the DOM after it was queried, or a handle whose CDP objectID went stale after page navigation.

Common situations: Handle grabbed before SPA hydration finishes; CSS animations leaving the element at 0x0; element inside a collapsed or hidden iframe; reusing an ElementHandle stored across page.reload() or navigation.

Related errors


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