grafana/k6 · error

parsing hover options: %w

Error message

parsing hover options: %w

What it means

The options object passed to frame.hover(selector, opts) failed to parse (frame_mapping.go:221). ElementHandleHoverOptions parses 'position' (must be {x: number, y: number}) and 'modifiers' (must be an array of strings); a value that cannot export to those types throws this error synchronously.

Source

Thrown at internal/js/modules/k6/browser/browser/frame_mapping.go:221

				f.Referrer(),
				f.NavigationTimeout(),
			)
			if err := gopts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing frame navigation options to %q: %w", url, err)
			}
			return promise(vu, func() (any, error) {
				resp, err := f.Goto(url, gopts)
				if err != nil {
					return nil, err //nolint:wrapcheck
				}

				return mapResponse(vu, resp), nil
			}), nil
		},
		"hover": func(selector string, opts sobek.Value) (*sobek.Promise, error) {
			popts := common.NewFrameHoverOptions(f.Timeout())
			if err := popts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing hover options: %w", err)
			}
			return promise(vu, func() (any, error) {
				return nil, f.Hover(selector, popts) //nolint:wrapcheck
			}), nil
		},
		"innerHTML": func(selector string, opts sobek.Value) (*sobek.Promise, error) {
			popts := common.NewFrameInnerHTMLOptions(f.Timeout())
			if err := popts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing inner HTML options: %w", err)
			}
			return promise(vu, func() (any, error) {
				return f.InnerHTML(selector, popts) //nolint:wrapcheck
			}), nil
		},
		"innerText": func(selector string, opts sobek.Value) (*sobek.Promise, error) {
			popts := common.NewFrameInnerTextOptions(f.Timeout())
			if err := popts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing inner text options: %w", err)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Use { position: { x: 10, y: 20 } } and { modifiers: ['Shift'] }
  2. Coerce JSON-sourced values before calling: position: { x: Number(p.x), y: Number(p.y) }
  3. Drop position entirely to hover the element center (the default)

Example fix

// before
frame.hover('#menu', { position: 'center' });
// after
frame.hover('#menu', { position: { x: 150, y: 30 } });
Defensive patterns

Strategy: type-guard

Validate before calling

const norm = {
  ...opts,
  ...(opts?.modifiers && !Array.isArray(opts.modifiers) ? { modifiers: String(opts.modifiers).split(',') } : {}),
  ...(opts?.position ? { position: { x: Number(opts.position.x), y: Number(opts.position.y) } } : {}),
};

Type guard

function isHoverOptions(o) {
  if (o == null) return true;
  const posOk = o.position == null || (typeof o.position === 'object' && !Array.isArray(o.position)
    && Number.isFinite(Number(o.position.x)) && Number.isFinite(Number(o.position.y)));
  const modOk = o.modifiers == null || (Array.isArray(o.modifiers) && o.modifiers.every(m => typeof m === 'string'));
  return posOk && modOk;
}

Try / catch

try {
  await frame.hover(sel, norm);
} catch (e) {
  if (String(e.message).includes('parsing hover options')) throw new Error(`bad hover opts for ${sel}: ${JSON.stringify(opts)}`);
  throw e;
}

Prevention

When it happens

Trigger: frame.hover('#el', { position: 'center' }), { position: [10, 10] }, or { modifiers: 'Shift' } (string not array) - the sobek ExportTo fails and gets wrapped as 'parsing hover options:'.

Common situations: Reusing click/dblclick option objects verbatim for hover; JSON-sourced configs where numbers arrive as strings; Playwright ports using tuple positions or CSS-style position keywords.

Related errors


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