grafana/k6 · error

hovering on element

Error message

hovering on element: %w

What it means

ElementHandle.hover() wraps the failure of a pointer action that scrolls the element into view, waits for it to be visible, stable, and able to receive pointer events, then moves the mouse to its center via the CDP Input domain. Timeouts on any of those waits, hit-test failures, or session errors get this prefix.

Solutions

  1. Hover the parent/menu trigger first, then hover the revealed child
  2. Wait for the element: await el.waitFor({ state: 'visible' }) before hovering
  3. Use force: true to skip actionability and hit testing: await el.hover({ force: true })
  4. Increase the timeout: await el.hover({ timeout: '60s' })
  5. Re-query the element handle if the page re-rendered

Example fix

// before
const sub = await page.$('.menu .submenu-item');
await sub.hover(); // submenu not open / parent not hovered

// after
await page.$('.menu .top-item').hover();
const sub = await page.$('.menu .submenu-item');
await sub.waitFor({ state: 'visible', timeout: '5s' });
await sub.hover();
Defensive patterns

Strategy: try-catch

Validate before calling

// Open the menu and wait for the child before hovering
await page.$('.menu .top-item').hover();
const child = await page.waitForSelector('.menu .submenu-item', { state: 'visible', timeout: '5s' });
await child.hover();

Try / catch

try {
  await el.hover({ timeout: '10s' });
} catch (e) {
  const msg = String(e.message);
  if (/timed out|would receive the event|not stable/i.test(msg)) {
    await el.hover({ force: true }); // skip actionability + hit test
  } else if (/Could not find object|not attached/i.test(msg)) {
    const fresh = await page.$(sel);
    await fresh.hover();
  } else throw e;
}

Prevention

When it happens

Trigger: Another element covers the target so the hit test fails ('other element would receive the event'); the element is hidden, zero-sized, or off-viewport; the element animates and never stabilizes; the handle is detached; the hover-capable parent is not hovered first so the child does not exist yet; the timeout option expires.

Common situations: Hover menus that only render after hovering the parent item; sticky headers/footers covering targets; banners and overlays; animation-heavy marketing pages; re-renders detaching the handle.

Related errors


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

Appendix: source

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

	s, ok := v.(string)
	if !ok {
		return "", false, fmt.Errorf(
			"getting attribute %q of element: unexpected type %T (expecting string)",
			name, v,
		)
	}

	return s, true, nil
}

// Hover scrolls element into view and hovers over its center point.
func (h *ElementHandle) Hover(opts *ElementHandleHoverOptions) error {
	hover := func(apiCtx context.Context, handle *ElementHandle, p *Position) (any, error) {
		return nil, handle.hover(apiCtx, p)
	}
	hoverAction := h.newPointerAction(hover, &opts.ElementHandleBasePointerOptions)
	if _, err := call(h.ctx, hoverAction, opts.Timeout); err != nil {
		return fmt.Errorf("hovering on element: %w", err)
	}

	applySlowMo(h.ctx)

	return nil
}

// InnerHTML returns the inner HTML of the element.
func (h *ElementHandle) InnerHTML() (string, error) {
	innerHTML := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
		return handle.innerHTML(apiCtx)
	}
	opts := NewElementHandleBaseOptions(h.DefaultTimeout())
	innerHTMLAction := h.newAction(
		[]string{}, innerHTML, opts.Force, withRetry, opts.NoWaitAfter, opts.Timeout,
	)
	v, err := call(h.ctx, innerHTMLAction, opts.Timeout)
	if err != nil {

View on GitHub (pinned to 3fcf5388d7)