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
- Re-locate the element immediately before the action instead of reusing an old handle
- Wait for visibility first: await page.waitForSelector(sel, { state: 'visible' }) or use a locator with auto-waiting
- Raise the action timeout option and/or browser.set_default_timeout for slow pages
- If the element is intentionally hidden and must still be clicked, pass { force: true } to skip actionability checks
- 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
- Always re-locate elements right before interacting; never cache handles across renders
- Wait for state 'visible' before pointer actions
- Treat zero-width/height elements as not-yet-clickable
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.