{"record":{"id":"b98db6c346d8f709","repo":"grafana/k6","slug":"parsing-frame-tap-options-w","errorCode":null,"errorMessage":"parsing frame tap options: %w","messagePattern":"parsing frame tap options: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/frame_mapping.go","lineNumber":382,"sourceCode":"\t\t\"setInputFiles\": func(selector string, files sobek.Value, opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tpopts, err := parseFrameSetInputFilesOptions(rt, opts, f.Timeout())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing setInputFiles options: %w\", err)\n\t\t\t}\n\n\t\t\tpfiles := new(common.Files)\n\t\t\tif err := pfiles.Parse(vu.Context(), files); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing setInputFiles parameter: %w\", err)\n\t\t\t}\n\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, f.SetInputFiles(selector, pfiles, popts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"tap\": func(selector string, opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tpopts, err := parseFrameTapOptions(rt, opts, f.Timeout())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing frame tap options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, f.Tap(selector, popts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"textContent\": func(selector string, opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tpopts, err := parseFrameTextContentOptions(rt, opts, f.Timeout())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing text content options: %w\", err)\n\t\t\t}\n\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\ts, ok, err := f.TextContent(selector, popts)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err //nolint:wrapcheck\n\t\t\t\t}\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, nil //nolint:nilnil","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/grafana/k6/blob/8d06114777191d21ef885d05c5f8aa9e1ed838ae/internal/js/modules/k6/browser/browser/frame_mapping.go#L364-L400","documentation":"Thrown by the k6 browser module ('k6/browser') while converting the JS options object of frame.tap(selector, options) into the Go FrameTapOptions (browser/frame_mapping.go:384). FrameTapOptions.Parse delegates to ElementHandleBasePointerOptions.Parse, which requires position to export to a numeric x/y map, and then parses modifiers via rt.ExportTo into []string; either conversion failure is wrapped with this message. plain values like timeout/strict/force/trial are coerced and cannot fail.","triggerScenarios":"frame.tap('#btn', { modifiers: 'Shift' }) (string instead of array); frame.tap('#btn', { position: [10, 20] }) or { position: 'center' } (position must be an object { x: number, y: number }). Same options reach this path via FrameLocator.tap and page taps on mobile emulation.","commonSituations":"Porting Playwright TypeScript scripts where an array or shorthand was used for position; building options dynamically from JSON.parse or environment variables so numbers arrive as strings; assuming Playwright accepts the same shapes k6 does.","solutions":["Make modifiers an array of strings: modifiers: ['Alt', 'Shift']","Give position as an object with numeric fields: position: { x: 10, y: 20 }","Remove the offending option to fall back to defaults (timeout defaults to the frame/page timeout)","Wrap dynamic option objects in Number()/Array.isArray() normalization before passing them"],"exampleFix":"// before\nframe.tap('#btn', { modifiers: 'Shift', position: [10, 20] });\n// after\nframe.tap('#btn', { modifiers: ['Shift'], position: { x: 10, y: 20 } });","handlingStrategy":"validation","validationCode":"const ok = (o = {}) =>\n  (o.modifiers == null || (Array.isArray(o.modifiers) && o.modifiers.every(m => typeof m === 'string'))) &&\n  (o.position == null || (typeof o.position === 'object' &&\n    typeof o.position.x === 'number' && typeof o.position.y === 'number'));\nif (!ok(opts)) throw new Error('invalid tap options');","typeGuard":"function isTapOptions(o) {\n  if (o == null) return true;\n  if (typeof o !== 'object' || Array.isArray(o)) return false;\n  if (o.modifiers != null && !(Array.isArray(o.modifiers) && o.modifiers.every(m => typeof m === 'string'))) return false;\n  if (o.position != null && !(typeof o.position.x === 'number' && typeof o.position.y === 'number')) return false;\n  return true;\n}","tryCatchPattern":"try { frame.tap(sel, opts); } catch (e) { if (!String(e.message).startsWith('parsing frame tap options')) throw e; /* normalize options and retry once */ }","preventionTips":["Keep one shared builder for pointer options that always emits {x, y} numbers and string arrays","Normalize any option object that came from JSON.parse or __ENV before passing it to browser APIs","Assert option shapes in a setup step of the script instead of inside hot iteration code"],"tags":["k6","k6-browser","frame","tap","options","type-mismatch"],"backgroundTag":"options-validation-failed","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"}