grafana/k6 · error

parsing locator tap options: %w

Error message

parsing locator tap options: %w

What it means

Thrown when locator.tap(opts) fails to parse options into FrameTapOptions. Two strict export paths make this a live error: position must export to map[string]float64 (numeric {x, y}), and modifiers must export to []string — so tap({ modifiers: 'Shift' }) fails with 'parsing locator tap options: <export error>'. Note the default timeout here comes from DefaultTimeout(), not the locator timeout. Accepted keys: position, modifiers array, trial, force, noWaitAfter, strict, timeout.

Source

Thrown at internal/js/modules/k6/browser/browser/locator_mapping.go:409

				return nil, fmt.Errorf("parsing type options: %w", err)
			}
			return promise(vu, func() (any, error) {
				return nil, lo.Type(text, copts) //nolint:wrapcheck
			}), nil
		},
		"hover": func(opts sobek.Value) (*sobek.Promise, error) {
			copts := common.NewFrameHoverOptions(lo.Timeout())
			if err := copts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing hover options: %w", err)
			}
			return promise(vu, func() (any, error) {
				return nil, lo.Hover(copts) //nolint:wrapcheck
			}), nil
		},
		"tap": func(opts sobek.Value) (*sobek.Promise, error) {
			copts := common.NewFrameTapOptions(lo.DefaultTimeout())
			if err := copts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing locator tap options: %w", err)
			}
			return promise(vu, func() (any, error) {
				return nil, lo.Tap(copts) //nolint:wrapcheck
			}), nil
		},
		"dispatchEvent": func(typ string, eventInit, opts sobek.Value) (*sobek.Promise, error) {
			popts := common.NewFrameDispatchEventOptions(lo.DefaultTimeout())
			if err := popts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing locator dispatch event options: %w", err)
			}
			return promise(vu, func() (any, error) {
				return nil, lo.DispatchEvent(typ, exportArg(eventInit), popts) //nolint:wrapcheck
			}), nil
		},
		"waitFor": func(opts sobek.Value) (*sobek.Promise, error) {
			copts := common.NewFrameWaitForSelectorOptions(lo.Timeout())
			if err := copts.Parse(vu.Context(), opts); err != nil {
				return nil, fmt.Errorf("parsing wait for options: %w", err)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Wrap modifiers in an array: tap({ modifiers: ['Shift', 'Meta'] }) or omit them
  2. Pass position as numeric {x, y} object only
  3. Coerce any string coordinates to Number before the call
  4. Remember tap uses the default action timeout, so pass timeout explicitly if needed

Example fix

// before
await page.locator('#btn').tap({ modifiers: 'Shift' });

// after
await page.locator('#btn').tap({ modifiers: ['Shift'] });
Defensive patterns

Strategy: validation

Validate before calling

function assertTapOpts(opts) {
  if (opts === null || opts === undefined) return;
  if (typeof opts !== 'object' || Array.isArray(opts)) {
    throw new TypeError('tap options must be a plain object');
  }
  if ('modifiers' in opts && opts.modifiers !== undefined) {
    if (!Array.isArray(opts.modifiers) || !opts.modifiers.every((m) => typeof m === 'string')) {
      throw new TypeError('tap modifiers must be an array of strings, e.g. ["Shift"]');
    }
  }
  if ('position' in opts && opts.position !== undefined) {
    const p = opts.position;
    if (typeof p !== 'object' || p === null || typeof p.x !== 'number' || typeof p.y !== 'number') {
      throw new TypeError('tap position must be { x: number, y: number }');
    }
  }
}

Type guard

function isTapOptions(v) {
  if (v === null || v === undefined) return true;
  if (typeof v !== 'object' || Array.isArray(v)) return false;
  const modsOk = v.modifiers === undefined ||
    (Array.isArray(v.modifiers) && v.modifiers.every((m) => typeof m === 'string'));
  const posOk = v.position === undefined ||
    (typeof v.position === 'object' && v.position !== null &&
      typeof v.position.x === 'number' && typeof v.position.y === 'number');
  return modsOk && posOk;
}

Try / catch

try {
  await locator.tap(opts);
} catch (e) {
  if (/parsing locator tap options/.test(String(e.message))) {
    throw new Error(`Bad tap options (modifiers must be string[], position must be {x,y} numbers): ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: tap({ modifiers: 'Shift' }) — string instead of ['Shift']; tap({ position: 'center' }) or { x: '10' }; tap({ modifiers: [1, 2] }) — non-string modifier entries.

Common situations: Mobile emulation scripts ported from Playwright where modifiers syntax differs; passing a single modifier string out of convenience; string coordinates from external data.

Related errors


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