{"record":{"id":"3e1b5569a553c214","repo":"grafana/k6","slug":"parsing-hover-options-w-3e1b55","errorCode":null,"errorMessage":"parsing hover options: %w","messagePattern":"parsing hover options: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/locator_mapping.go","lineNumber":400,"sourceCode":"\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, lo.PressSequentially(text, copts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\n\t\t\"type\": func(text string, opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tcopts := common.NewFrameTypeOptions(lo.Timeout())\n\t\t\tif err := copts.Parse(vu.Context(), opts); err != nil {\n\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)","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/browser/locator_mapping.go#L382-L418","documentation":"Thrown when locator.hover(opts) fails to parse options into FrameHoverOptions, which embeds ElementHandleBasePointerOptions. Unlike the lenient base parser, the pointer parser strictly exports the position key to map[string]float64 via rt.ExportTo, so hover({ position: ... }) with a non-object or non-numeric coordinates is a live error path: 'parsing hover options: <export error>'. Accepted keys: position {x: number, y: number}, trial boolean, timeout number ms, force boolean, noWaitAfter boolean.","triggerScenarios":"hover({ position: 'center' }) — position must be explicit coordinates, the 'center' string is not supported; hover({ position: { x: '100', y: 100 } }) — string coordinates; hover({ position: [100, 100] }) — array instead of {x, y} object.","commonSituations":"Porting Playwright examples where 'center' is sometimes documented as behavior rather than a literal; data-driven coordinates arriving as strings from JSON fixtures; assuming array tuples work as positions.","solutions":["Omit position to hover the element center: hover({ timeout: 5000 })","Or pass explicit numeric coordinates: hover({ position: { x: 100, y: 100 } })","Coerce string coordinates to Number before passing","Keep only supported keys: position, trial, force, noWaitAfter, timeout"],"exampleFix":"// before\nawait page.locator('#canvas').hover({ position: 'center' });\n\n// after\nawait page.locator('#canvas').hover(); // center by default\n// or\nawait page.locator('#canvas').hover({ position: { x: 100, y: 100 } });","handlingStrategy":"validation","validationCode":"function assertHoverOpts(opts) {\n  if (opts === null || opts === undefined) return;\n  if (typeof opts !== 'object' || Array.isArray(opts)) {\n    throw new TypeError('hover options must be a plain object');\n  }\n  if ('position' in opts && opts.position !== undefined) {\n    const p = opts.position;\n    if (typeof p !== 'object' || p === null || Array.isArray(p)) {\n      throw new TypeError(\"hover position must be { x: number, y: number }; strings like 'center' are not supported\");\n    }\n    if (typeof p.x !== 'number' || typeof p.y !== 'number') {\n      throw new TypeError('hover position x and y must be numbers');\n    }\n  }\n}","typeGuard":"function isPointerPosition(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) &&\n    typeof v.x === 'number' && Number.isFinite(v.x) &&\n    typeof v.y === 'number' && Number.isFinite(v.y);\n}","tryCatchPattern":"try {\n  await locator.hover(opts);\n} catch (e) {\n  if (/parsing hover options/.test(String(e.message))) {\n    throw new Error(`Bad hover options (check position: {x,y} numbers only): ${e.message}`);\n  }\n  throw e;\n}","preventionTips":["Hover without position to target the element center","Always express positions as { x: number, y: number }, never strings, arrays, or 'center'","Coerce coordinates from external data with Number() before passing"],"tags":["k6","browser","locator","options","hover","position"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}