{"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":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/locator_mapping.go","lineNumber":410,"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, err := parseFrameHoverOptions(rt, opts, lo.Timeout())\n\t\t\tif 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, err := parseFrameTapOptions(rt, opts, lo.DefaultTimeout())\n\t\t\tif 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, err := parseFrameDispatchEventOptions(rt, opts, lo.DefaultTimeout())\n\t\t\tif 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, err := parseFrameWaitForSelectorOptions(rt, opts, lo.Timeout())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing wait for options: %w\", err)","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/grafana/k6/blob/8d06114777191d21ef885d05c5f8aa9e1ed838ae/internal/js/modules/k6/browser/browser/locator_mapping.go#L392-L428","documentation":"Thrown synchronously when the options argument to locator.tap(opts) fails to parse. The mapping (locator_mapping.go:406-410) builds common.FrameTapOptions — notably seeded with lo.DefaultTimeout() rather than the locator timeout — and wraps Parse failures as \"parsing locator tap options: %w\". Parse (frame_options.go:571-592) runs the pointer base (position exported to map[string]float64, trial, force, noWaitAfter, timeout) then exports \"modifiers\" to []string; those ExportTo conversions are the realistic failure points.","triggerScenarios":"locator.tap({ position: 'center' }) or position: { x: '10', y: 20 } with string coordinates; position that is not an object; modifiers: 'Shift' instead of an array; modifiers containing numbers; throwing getters or Proxy traps on options.","commonSituations":"Mobile emulation tests tapping at coordinates computed from element bounding boxes where values become strings; passing Playwright-style option objects with a bare-string modifiers; helper functions that conditionally build position and sometimes pass undefined fields.","solutions":["Pass position as { x: <number>, y: <number> } with numeric values relative to the element's top-left corner","Pass modifiers as an array of modifier key strings, e.g. ['Alt', 'Shift']","Coerce computed coordinates with Number() before building the options object","Validate the options shape before calling tap when coordinates come from external data; try/catch otherwise"],"exampleFix":"// before\nconst p = await el.getAttribute('data-tap'); // \"12,34\"\nconst [x, y] = p.split(',');\nawait locator.tap({ position: { x, y } }); // strings -> parse error\n\n// after\nconst [x, y] = p.split(',').map(Number);\nawait locator.tap({ position: { x, y } });","handlingStrategy":"validation","validationCode":"function assertTapOptions(opts) {\n  if (opts == null) return;\n  if (typeof opts !== 'object' || Array.isArray(opts)) throw new TypeError('tap options must be an object');\n  if (opts.position != null) {\n    const p = opts.position;\n    if (typeof p !== 'object' || !Number.isFinite(p.x) || !Number.isFinite(p.y))\n      throw new TypeError(`position must be { x: number, y: number }, got ${JSON.stringify(p)}`);\n  }\n  if (opts.modifiers != null) {\n    if (!Array.isArray(opts.modifiers) || opts.modifiers.some(m => typeof m !== 'string'))\n      throw new TypeError('modifiers must be an array of strings');\n  }\n}\nassertTapOptions(opts);\nawait locator.tap(opts);","typeGuard":"function isTapOptions(o) {\n  if (o == null) return true;\n  if (typeof o !== 'object') return false;\n  const pOk = o.position === undefined ||\n    (typeof o.position === 'object' && Number.isFinite(o.position.x) && Number.isFinite(o.position.y));\n  const mOk = o.modifiers === undefined ||\n    (Array.isArray(o.modifiers) && o.modifiers.every(m => typeof m === 'string'));\n  return pOk && mOk;\n}","tryCatchPattern":"try { await locator.tap(opts); } catch (e) { if (String(e).includes('parsing locator tap options')) throw new Error(`bad tap position/modifiers: ${JSON.stringify(opts)}`); throw e; }","preventionTips":["Convert tap coordinates from external sources with Number() before the call","Pass modifiers as an array of strings like ['Alt','Shift']","Remember tap uses the browser default timeout unless you pass timeout explicitly","Validate computed positions when data comes from attributes or APIs"],"tags":["k6","browser","locator","tap","options","position","modifiers"],"backgroundTag":"invalid-options-object","analyzedSha":"8d06114777191d21ef885d05c5f8aa9e1ed838ae","analyzedAt":"2026-09-14T20:10:14.619Z","contentChangedAt":"2026-09-14T20:10:14.619Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}