grafana/k6 · error
hovering on %q: %w
Error message
hovering on %q: %w
What it means
Thrown by Locator.Hover() when the wrapped frame.hover call fails. Strict mode is forced on and the call retries, so the element must resolve to exactly one element and become hoverable (visible, stable, receiving pointer events) before the timeout. Common failures: another element overlays the target and intercepts pointer events, strict-mode violation, or timeout.
Source
Thrown at internal/js/modules/k6/browser/common/locator.go:605
opts.Strict = true
if err := l.frame.typ(l.selector, text, opts); err != nil {
return spanRecordErrorf(span, "typing %q in %q: %w", text, l.selector, err)
}
applySlowMo(l.ctx)
return nil
}
// Hover moves the pointer over the element that matches the locator's
// selector with strict mode on.
func (l *Locator) Hover(opts *FrameHoverOptions) error {
l.log.Debugf("Locator:Hover", "fid:%s furl:%q sel:%q opts:%+v", l.frame.ID(), l.frame.URL(), l.selector, opts)
opts.Strict = true
opts.retry = true
if err := l.frame.hover(l.selector, opts); err != nil {
return fmt.Errorf("hovering on %q: %w", l.selector, err)
}
applySlowMo(l.ctx)
return nil
}
// Tap the element found that matches the locator's selector with strict mode on.
func (l *Locator) Tap(opts *FrameTapOptions) error {
l.log.Debugf("Locator:Tap", "fid:%s furl:%q sel:%q opts:%+v", l.frame.ID(), l.frame.URL(), l.selector, opts)
opts.Strict = true
opts.retry = true
if err := l.frame.tap(l.selector, opts); err != nil {
return fmt.Errorf("tapping on %q: %w", l.selector, err)
}
applySlowMo(l.ctx)View on GitHub (pinned to 93accf6570)
Solutions
- Dismiss or wait for overlays (spinners, banners) to disappear before hovering
- Expand the parent container/tab so the element is visible and stable
- Use a unique selector matching exactly one element
- As a last resort pass force: true in opts to skip actionability checks, or increase timeout
Example fix
// before
await page.locator('.menu-item').hover(); // covered by cookie banner
// after
const banner = page.locator('#cookie-banner');
if (await banner.count()) await banner.locator('button.accept').click();
await page.locator('.menu-item').hover(); Defensive patterns
Strategy: try-catch
Validate before calling
const el = page.locator('a.menu-item');
await el.waitFor({ state: 'visible' });
await el.hover(); Type guard
async function isUnobstructed(page, sel) {
const handle = await page.$(sel);
if (!handle) return false;
const box = await handle.boundingBox();
if (!box) return false;
const hit = await handle.evaluate((n, x, y) => {
const t = document.elementFromPoint(x, y);
return !!t && (t === n || n.contains(t));
}, box.x + box.width / 2, box.y + box.height / 2);
return hit;
} Try / catch
try {
await page.locator(sel).hover();
} catch (e) {
if (/intercepts pointer events|not stable|timeout/i.test(e.message)) {
await dismissOverlays(page); // app-specific helper
await page.locator(sel).hover();
} else throw e;
} Prevention
- Dismiss cookie banners/spinners before hover assertions
- Disable CSS animations in test builds for stability checks
- Prefer locator.hover() over mouse.move()+down for UI elements
When it happens
Trigger: locator.hover() where the selector matches 0 or >1 elements; the element is hidden or has zero size; an overlay (cookie banner, spinner, sticky header) covers it so pointer events are intercepted; the element keeps moving (animation) past the timeout.
Common situations: Hovering menu items under a sticky navbar or modal overlay; hovering elements inside accordions/tabs that are not yet expanded; CSS animations preventing the stability check.
Related errors
- tapping on %q: %w
- parsing hover options: %w
- hovering on element: %w
- getting input value of %q: %w
- selecting option on %q: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/87402e09873b79d1.
Report an issue: GitHub.