{"record":{"id":"fe554469cd26f126","repo":"grafana/k6","slug":"getting-attribute-q-of-element-w","errorCode":null,"errorMessage":"getting attribute %q of element: %w","messagePattern":"getting attribute %q of element: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":955,"sourceCode":"\tapplySlowMo(h.ctx)\n\n\treturn nil\n}\n\n// GetAttribute retrieves the value of specified element attribute.\n// The second return value is true if the attribute exists, and false otherwise.\nfunc (h *ElementHandle) GetAttribute(name string) (string, bool, error) {\n\tgetAttribute := func(apiCtx context.Context, handle *ElementHandle) (any, error) {\n\t\treturn handle.getAttribute(apiCtx, name)\n\t}\n\topts := NewElementHandleBaseOptions(h.DefaultTimeout())\n\tgetAttributeAction := h.newAction(\n\t\t[]string{}, getAttribute, opts.Force, withRetry, opts.NoWaitAfter, opts.Timeout,\n\t)\n\n\tv, err := call(h.ctx, getAttributeAction, opts.Timeout)\n\tif err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"getting attribute %q of element: %w\", name, err)\n\t}\n\tif v == nil {\n\t\treturn \"\", false, nil\n\t}\n\ts, ok := v.(string)\n\tif !ok {\n\t\treturn \"\", false, fmt.Errorf(\n\t\t\t\"getting attribute %q of element: unexpected type %T (expecting string)\",\n\t\t\tname, v,\n\t\t)\n\t}\n\n\treturn s, true, nil\n}\n\n// Hover scrolls element into view and hovers over its center point.\nfunc (h *ElementHandle) Hover(opts *ElementHandleHoverOptions) error {\n\thover := func(apiCtx context.Context, handle *ElementHandle, p *Position) (any, error) {","sourceCodeStart":937,"sourceCodeEnd":973,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/element_handle.go#L937-L973","documentation":"ElementHandle.getAttribute() wraps the failure of a CDP evaluation that runs element.getAttribute(name) with returnByValue. A missing attribute is NOT an error (the JS API returns null for that); this error means the evaluation itself failed - detached handle, destroyed execution context, timeout - or the attribute name broke the injected script, since the name is interpolated directly into the JavaScript source string.","triggerScenarios":"The attribute name contains a single quote, backslash, or newline, which breaks the generated JS ('element.getAttribute('weird'name')'); the element handle is detached or the execution context was destroyed by navigation; the evaluation exceeds the default timeout; the session/page closed mid-call.","commonSituations":"Attribute names built dynamically from test data that contain quotes or whitespace; SPAs re-rendering between query and read; reusing cached handles after page.goto().","solutions":["Sanitize the attribute name: allow only /^[A-Za-z_:][-A-Za-z0-9_:.]*$/ before calling getAttribute","Re-acquire the element handle after any navigation or re-render","For exotic names, read via evaluation instead: await el.evaluate((e, n) => e.getAttribute(n), name)","Increase the timeout via options or K6_BROWSER_TIMEOUT if the session is slow"],"exampleFix":"// before\nconst name = getDataAttr(); // \"data-q'tag\" -> breaks injected JS\nconst v = await el.getAttribute(name);\n\n// after\nconst name = getDataAttr();\nif (!/^[A-Za-z_:][-A-Za-z0-9_:.]*$/.test(name)) {\n  const v = await el.evaluate((e, n) => e.getAttribute(n), name);\n} else {\n  const v = await el.getAttribute(name);\n}","handlingStrategy":"validation","validationCode":"// Validate the attribute name (it is interpolated into injected JS) and handle freshness\nconst NAME = /^[A-Za-z_:][-A-Za-z0-9_:.]*$/;\nif (!NAME.test(name)) {\n  const v = await el.evaluate((e, n) => e.getAttribute(n), name);\n} else {\n  const v = await el.getAttribute(name); // null when missing, never an error\n}","typeGuard":null,"tryCatchPattern":"try {\n  const v = await el.getAttribute(name);\n} catch (e) {\n  if (/Could not find object|Execution context|timed out/i.test(String(e.message))) {\n    const fresh = await page.$(sel);\n    const v = await fresh.getAttribute(name);\n  } else throw e;\n}","preventionTips":["Treat null as the normal 'attribute missing' result; an exception means the read failed","Sanitize dynamically-built attribute names before passing them in","Use el.evaluate() for attribute names containing quotes or unusual characters"],"tags":["browser","get-attribute","dom","stale-element","injection","k6"],"backgroundTag":"stale-element-reference","analyzedSha":"3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}