{"record":{"id":"c341ce3754dcb788","repo":"grafana/k6","slug":"parsing-locator-tap-options-w","errorCode":null,"errorMessage":"parsing locator tap options: %w","messagePattern":"parsing locator tap options: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/locator_mapping.go","lineNumber":409,"sourceCode":"\t\t\t\treturn nil, fmt.Errorf(\"parsing type options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, lo.Type(text, copts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"hover\": func(opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tcopts := common.NewFrameHoverOptions(lo.Timeout())\n\t\t\tif err := copts.Parse(vu.Context(), opts); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing hover options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, lo.Hover(copts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"tap\": func(opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tcopts := common.NewFrameTapOptions(lo.DefaultTimeout())\n\t\t\tif err := copts.Parse(vu.Context(), opts); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing locator tap options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, lo.Tap(copts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"dispatchEvent\": func(typ string, eventInit, opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tpopts := common.NewFrameDispatchEventOptions(lo.DefaultTimeout())\n\t\t\tif err := popts.Parse(vu.Context(), opts); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing locator dispatch event options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, lo.DispatchEvent(typ, exportArg(eventInit), popts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"waitFor\": func(opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tcopts := common.NewFrameWaitForSelectorOptions(lo.Timeout())\n\t\t\tif err := copts.Parse(vu.Context(), opts); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing wait for options: %w\", err)","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/browser/locator_mapping.go#L391-L427","documentation":"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.","triggerScenarios":"tap({ modifiers: 'Shift' }) — string instead of ['Shift']; tap({ position: 'center' }) or { x: '10' }; tap({ modifiers: [1, 2] }) — non-string modifier entries.","commonSituations":"Mobile emulation scripts ported from Playwright where modifiers syntax differs; passing a single modifier string out of convenience; string coordinates from external data.","solutions":["Wrap modifiers in an array: tap({ modifiers: ['Shift', 'Meta'] }) or omit them","Pass position as numeric {x, y} object only","Coerce any string coordinates to Number before the call","Remember tap uses the default action timeout, so pass timeout explicitly if needed"],"exampleFix":"// before\nawait page.locator('#btn').tap({ modifiers: 'Shift' });\n\n// after\nawait page.locator('#btn').tap({ modifiers: ['Shift'] });","handlingStrategy":"validation","validationCode":"function assertTapOpts(opts) {\n  if (opts === null || opts === undefined) return;\n  if (typeof opts !== 'object' || Array.isArray(opts)) {\n    throw new TypeError('tap options must be a plain object');\n  }\n  if ('modifiers' in opts && opts.modifiers !== undefined) {\n    if (!Array.isArray(opts.modifiers) || !opts.modifiers.every((m) => typeof m === 'string')) {\n      throw new TypeError('tap modifiers must be an array of strings, e.g. [\"Shift\"]');\n    }\n  }\n  if ('position' in opts && opts.position !== undefined) {\n    const p = opts.position;\n    if (typeof p !== 'object' || p === null || typeof p.x !== 'number' || typeof p.y !== 'number') {\n      throw new TypeError('tap position must be { x: number, y: number }');\n    }\n  }\n}","typeGuard":"function isTapOptions(v) {\n  if (v === null || v === undefined) return true;\n  if (typeof v !== 'object' || Array.isArray(v)) return false;\n  const modsOk = v.modifiers === undefined ||\n    (Array.isArray(v.modifiers) && v.modifiers.every((m) => typeof m === 'string'));\n  const posOk = v.position === undefined ||\n    (typeof v.position === 'object' && v.position !== null &&\n      typeof v.position.x === 'number' && typeof v.position.y === 'number');\n  return modsOk && posOk;\n}","tryCatchPattern":"try {\n  await locator.tap(opts);\n} catch (e) {\n  if (/parsing locator tap options/.test(String(e.message))) {\n    throw new Error(`Bad tap options (modifiers must be string[], position must be {x,y} numbers): ${e.message}`);\n  }\n  throw e;\n}","preventionTips":["Always wrap modifiers in an array: ['Shift'], never 'Shift'","Express tap positions as numeric { x, y }","Remember tap uses the default action timeout; set timeout explicitly when needed"],"tags":["k6","browser","locator","options","tap","modifiers"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}