{"record":{"id":"059b216195916d8f","repo":"grafana/k6","slug":"parsing-get-attribute-options-w","errorCode":null,"errorMessage":"parsing get attribute options: %w","messagePattern":"parsing get attribute options: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/locator_mapping.go","lineNumber":221,"sourceCode":"\t\t\t}))\n\t\t},\n\t\t\"first\": func() *sobek.Object {\n\t\t\tml := mapLocator(vu, lo.First())\n\t\t\treturn rt.ToValue(ml).ToObject(rt)\n\t\t},\n\t\t\"focus\": func(opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tcopts := common.NewFrameBaseOptions(lo.Timeout())\n\t\t\tif err := copts.Parse(vu.Context(), opts); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing focus options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\treturn nil, lo.Focus(copts) //nolint:wrapcheck\n\t\t\t}), nil\n\t\t},\n\t\t\"getAttribute\": func(name string, opts sobek.Value) (*sobek.Promise, error) {\n\t\t\tcopts := common.NewFrameBaseOptions(lo.Timeout())\n\t\t\tif err := copts.Parse(vu.Context(), opts); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing get attribute options: %w\", err)\n\t\t\t}\n\t\t\treturn promise(vu, func() (any, error) {\n\t\t\t\ts, ok, err := lo.GetAttribute(name, copts)\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\n\t\t\t\t}\n\t\t\t\treturn s, nil\n\t\t\t}), nil\n\t\t},\n\t\t\"getByAltText\": func(alt sobek.Value, opts sobek.Value) (*sobek.Object, error) {\n\t\t\tif k6common.IsNullish(alt) {\n\t\t\t\treturn nil, errors.New(\"missing required argument 'altText'\")\n\t\t\t}\n\t\t\tpalt, popts := parseGetByBaseOptions(vu.Context(), alt, false, opts)\n","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/browser/locator_mapping.go#L203-L239","documentation":"Thrown when locator.getAttribute(name, opts) cannot parse its second argument into FrameBaseOptions (accepted keys: strict boolean, timeout number in milliseconds). The mapping layer wraps the Parse failure as 'parsing get attribute options: <cause>' so the exception names the failing API. In current k6 the underlying FrameBaseOptions.Parse is lenient (unknown keys ignored, values coerced via ToBoolean/ToInteger), so this wrap is mostly defensive; it fires when the opts slot gets something that is not a valid options shape, especially on k6 versions with stricter option parsing.","triggerScenarios":"Calling page.locator('a').getAttribute('href', opts) with a non-object in the options slot, e.g. getAttribute('href', '30s') or getAttribute('href', 30000); or passing a wrong-typed timeout/strict under a strict parser. Playwright's getAttribute takes no options object, so ported scripts sometimes pass extra positional arguments that land here.","commonSituations":"Porting Playwright scripts and passing a timeout string instead of an options object; argument-order mistakes (selector in the opts slot); upgrading/downgrading k6 browser module versions whose parser rejects wrong field types instead of coercing them.","solutions":["Pass options as a plain object: locator.getAttribute('href', { timeout: 30000 }) — timeout in milliseconds as a number, or omit the second argument entirely","Check argument order: getAttribute(name, opts); a string like '30s' in the opts slot is never valid","If you only need a timeout, set it once via locator.setDefaultTimeout() or page.setDefaultTimeout() instead of per-call options","Upgrade to the latest k6 so unknown/loosely-typed keys are coerced or ignored rather than rejected"],"exampleFix":"// before\nconst href = await page.locator('a.link').getAttribute('href', '30s');\n\n// after\nconst href = await page.locator('a.link').getAttribute('href', { timeout: 30000 });","handlingStrategy":"validation","validationCode":"function assertGetAttributeOpts(opts) {\n  if (opts === null || opts === undefined) return;\n  if (typeof opts !== 'object' || Array.isArray(opts)) {\n    throw new TypeError('getAttribute options must be a plain object');\n  }\n  if ('timeout' in opts && (typeof opts.timeout !== 'number' || !Number.isFinite(opts.timeout))) {\n    throw new TypeError('getAttribute timeout must be a number of milliseconds');\n  }\n}","typeGuard":"function isLocatorBaseOptions(v) {\n  if (v === null || v === undefined) return true;\n  if (typeof v !== 'object' || Array.isArray(v)) return false;\n  return (v.timeout === undefined || typeof v.timeout === 'number') &&\n         (v.strict === undefined || typeof v.strict === 'boolean');\n}","tryCatchPattern":"try {\n  const href = await locator.getAttribute('href', opts);\n} catch (e) {\n  if (/parsing get attribute options/.test(String(e.message))) {\n    throw new Error(`Invalid options passed to locator.getAttribute: ${JSON.stringify(opts)}`);\n  }\n  throw e;\n}","preventionTips":["Always pass option objects as literals with numeric millisecond timeouts","Keep a shared typed helper for building { timeout } objects so every locator call uses the same shape","Lint against duration strings ('5s') in browser module calls"],"tags":["k6","browser","locator","options","getattribute"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}