{"record":{"id":"1a9c9786a36ad5e9","repo":"grafana/k6","slug":"error-while-parsing-selector-selector-canno","errorCode":null,"errorMessage":"Error while parsing selector `${selector}` - cannot use ${operator} in attribute with regular expression","messagePattern":"Error while parsing selector `(.+?)` - cannot use (.+?) in attribute with regular expression","errorType":"exception","errorClass":"InvalidSelectorError","httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/js/injected_script.js","lineNumber":1092,"sourceCode":"    const jsonPath = [];\n    jsonPath.push(readAttributeToken());\n    skipSpaces();\n    while (next() === \".\") {\n      eat1();\n      jsonPath.push(readAttributeToken());\n      skipSpaces();\n    }\n    if (next() === \"]\") {\n      eat1();\n      return { name: jsonPath.join(\".\"), jsonPath, op: \"<truthy>\", value: null, caseSensitive: false };\n    }\n    const operator = readOperator();\n    let value = void 0;\n    let caseSensitive = true;\n    skipSpaces();\n    if (next() === \"/\") {\n      if (operator !== \"=\")\n        throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with regular expression`);\n      value = readRegularExpression();\n    } else if (next() === `'` || next() === `\"`) {\n      value = readQuotedString(next()).slice(1, -1);\n      skipSpaces();\n      if (next() === \"i\" || next() === \"I\") {\n        caseSensitive = false;\n        eat1();\n      } else if (next() === \"s\" || next() === \"S\") {\n        caseSensitive = true;\n        eat1();\n      }\n    } else {\n      value = \"\";\n      while (!EOL && (isCSSNameChar(next()) || next() === \"+\" || next() === \".\"))\n        value += eat1();\n      if (value === \"true\") {\n        value = true;\n      } else if (value === \"false\") {","sourceCodeStart":1074,"sourceCodeEnd":1110,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/js/injected_script.js#L1074-L1110","documentation":"Thrown as a GoError JavaScript exception by Selection.adjacentUntil (js/modules/k6/html/html.go:128-164), the shared implementation for nextUntil(), prevUntil() and parentsUntil(). After switching on the argument count (0, 1 or 2) and on def[0].Export() accepting only string, Selection or nil, any other type falls out of the inner switch and hits the panic at line 163-164. Unlike varargFnCall-based methods (add/find/closest/has), this path does NOT accept an Element, and the 2-argument form coerces the filter with def[1].String() while the first argument must still be string/Selection/nil.","triggerScenarios":"Calling `sel.nextUntil(x)`, `sel.prevUntil(x)` or `sel.parentsUntil(x)` where x is an Element (e.g. `sel.get(0)`), a number, boolean, plain object or array. Also `sel.parentsUntil(elem, '.filter')` with two args and an Element/number first argument — the switch falls through both for the 1-arg and 2-arg forms.","commonSituations":"Porting jQuery traversals that pass DOM nodes to *Until methods; mixing up Element (accepted by find/add) with Selection (the only object type accepted here); passing an index or a boolean as the until-boundary; undefined boundary variables from shared config.","solutions":["Use a selector string: `sel.nextUntil('h2')`","Use a Selection as boundary: `sel.nextUntil(doc.find('h2'))`","Use null/undefined (or no argument) to mean 'until the end': `sel.nextUntil(null)`","Do not pass an Element here — if you hold `sel.get(0)`, go through a selector or Selection instead","Guard dynamic boundaries: `const until = (typeof b === 'string' || b instanceof Object) ? b : null;` before calling"],"exampleFix":"// before\nconst boundary = doc.find('h2').get(0); // an Element — not accepted by adjacentUntil\nitems.prevUntil(boundary); // GoError: cannot use a '*html.Element' as a selector\n\n// after\nconst boundary = doc.find('h2'); // keep the Selection\nitems.prevUntil(boundary);\n// or use the selector string directly:\nitems.prevUntil('h2');","handlingStrategy":"type-guard","validationCode":"const isUntilArg = (a) =>\n  a == null || // until the end\n  typeof a === 'string' || // selector\n  (typeof a === 'object' && typeof a.find === 'function'); // Selection — Element is NOT valid\n\nfunction safeNextUntil(sel, boundary) {\n  if (!isUntilArg(boundary)) throw new Error(`invalid until-argument: ${typeof boundary}`);\n  return sel.nextUntil(boundary);\n}","typeGuard":"function isUntilBoundary(a) {\n  if (a == null) return true;\n  if (typeof a === 'string') return true;\n  return a && typeof a === 'object' && typeof a.find === 'function' && typeof a.size === 'number';\n}","tryCatchPattern":"try {\n  sel.nextUntil(arg);\n} catch (e) {\n  if (/cannot use a '.*' as a selector/.test(String(e.message))) {\n    sel.nextUntil(); // unbounded traversal as fallback\n  } else {\n    throw e;\n  }\n}","preventionTips":["nextUntil/prevUntil/parentsUntil accept only a selector string, a Selection, or nothing — never an Element or number","If you hold an Element (sel.get(0)), re-derive a Selection or use a selector string instead","The optional second argument is coerced with String(), but the FIRST argument still decides validity — validate it separately"],"tags":["html","selector","traversal","goquery","type-error","javascript"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}