{"record":{"id":"11de44467d4227f9","repo":"grafana/k6","slug":"malformed-attribute-selector-selector","errorCode":null,"errorMessage":"Malformed attribute selector: ${selector}","messagePattern":"Malformed attribute selector: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/js/injected_script.js","lineNumber":1614,"sourceCode":"      if (node.nodeType === 1 /*Node.ELEMENT_NODE*/) {\n        result.push(node);\n      }\n    }\n    return result;\n  }\n}\n\nclass AttributeEngine {\n  constructor() {\n    this._evaluator = new SelectorEvaluatorImpl();\n  }\n  queryAll(root, selector) {\n    try {\n      this._evaluator.begin();\n\n      const parsed = parseAttributeSelector(selector, true);\n      if (parsed.name || parsed.attributes.length !== 1)\n        throw new Error(\"Malformed attribute selector: \" + selector);\n      const { name, value, caseSensitive } = parsed.attributes[0];\n      const lowerCaseValue = caseSensitive ? null : value.toLowerCase();\n      let matcher;\n      if (value instanceof RegExp)\n        matcher = (s) => !!s.match(value);\n      else if (caseSensitive)\n        matcher = (s) => s === value;\n      else\n        matcher = (s) => s.toLowerCase().includes(lowerCaseValue);\n      const elements = this._evaluator._queryCSS({ scope: root, pierceShadow: true }, `[${name}]`);\n      return elements.filter((e) => matcher(e.getAttribute(name)));\n    } finally {\n      this._evaluator.end();\n    }\n  };\n}\n\nclass LabelEngine {","sourceCodeStart":1596,"sourceCodeEnd":1632,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/js/modules/k6/browser/common/js/injected_script.js#L1596-L1632","documentation":"Frame.IsEditable() (frame.go:1414) wraps the isEditable action failure as 'checking is %q editable'. The action resolves the selector to an attached element within Timeout and asks the page (via the injected utility) for the element's 'editable' state using a zero timeout, so the state check itself does not wait. Errors wrapped here are selector timeouts, strict-mode violations, frame/context destruction, or injected-script failures.","triggerScenarios":"No element matches before opts.Timeout; multiple matches with Strict:true; frame navigates or detaches during the check; the injected isEditable evaluation errors on exotic shadow-DOM/custom elements.","commonSituations":"Asserting form fields are editable before typing in e2e smoke checks; checking fields that render after async hydration; iframes re-rendering mid-check; strict mode newly enabled after a k6 upgrade.","solutions":["Wait for the element first: await page.waitForSelector(sel, { state: 'attached', timeout: 10000 }).","Use unique selectors (data-testid) to avoid strict-mode violations.","Run the check on the frame that contains the element.","Increase the per-call timeout on slow pages: page.isEditable(sel, { timeout: 60000 })."],"exampleFix":"// before\nconst ok = await page.isEditable('#comment');\n\n// after\nawait page.waitForSelector('#comment', { state: 'attached', timeout: 10000 });\nconst ok = await page.isEditable('#comment');","handlingStrategy":"try-catch","validationCode":"await page.waitForSelector('#comment', { state: 'attached', timeout: 5000 });","typeGuard":null,"tryCatchPattern":"try {\n  return await page.isEditable(sel, { timeout: 10000 });\n} catch (e) {\n  if (String(e).includes('timed out')) throw new Error(`field never appeared: ${sel}`);\n  throw e;\n}","preventionTips":["Wait for the field to attach before state checks.","Use unique selectors under strict mode.","Check state on the owning frame."],"tags":["k6","browser","frame","iseditable","selector","timeout"],"backgroundTag":"element-not-found-timeout","analyzedSha":"01ffac6f245854c1b8adc6a69857c76a15f90022","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}