grafana/k6 · error

hovering on element: %w

Error message

hovering on element: %w

What it means

Returned by ElementHandle.hover() when the pointer action fails. Hover is a pointer action: before moving the mouse, k6 waits for the element to be visible, stable (not animating), and to actually receive pointer events at the hover point. Any of those checks timing out, a stale handle, or a CDP error is wrapped with %w here.

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

Solutions

  1. Open the parent menu / wait for visibility first: page.waitForSelector(sel, { state: 'visible' })
  2. Disable or settle CSS animations before hovering (inject a style rule or wait for the transition to end)
  3. Use { force: true } when an overlay legitimately covers the element and you just need the mouse move
  4. Pass an explicit { timeout: ms } larger than the animation duration
  5. Re-query the handle right before hover to avoid staleness

Example fix

// before
await page.hover('#menu-item');
// after
await page.waitForSelector('#menu-item', { state: 'visible' });
await page.hover('#menu-item', { timeout: 10000 });
// or, when an overlay intentionally covers the item
await page.hover('#menu-item', { force: true });
Defensive patterns

Strategy: try-catch

Validate before calling

const sel = '#menu-item';
await page.waitForSelector(sel, { state: 'visible', timeout: 5000 });
const visible = await (await page.$(sel)).isVisible();
if (visible) {
  await page.hover(sel, { timeout: 10000 });
}

Try / catch

try {
  await handle.hover();
} catch (e) {
  if (String(e).includes('hovering on element')) {
    await page.waitForSelector(sel, { state: 'visible' });
    await page.hover(sel, { force: true });
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Hovering a hidden element; hovering an element that is still animating/moving so it never becomes 'stable'; another element (sticky header, cookie banner, overlay) intercepting pointer events at the target point; a stale handle after re-render; a Position option that lands outside the element.

Common situations: Hover menus that only open after another hover (element not yet visible); CSS transitions that never settle (infinite animation); fixed/sticky headers covering the target in small viewports; SPAs re-rendering between query and hover.

Related errors


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