grafana/k6 · error · InvalidSelectorError
Error while parsing selector `${selector}` - cannot use ${op
Error message
Error while parsing selector `${selector}` - cannot use ${operator} in attribute with regular expression What it means
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.
Source
Thrown at internal/js/modules/k6/browser/common/js/injected_script.js:1092
const jsonPath = [];
jsonPath.push(readAttributeToken());
skipSpaces();
while (next() === ".") {
eat1();
jsonPath.push(readAttributeToken());
skipSpaces();
}
if (next() === "]") {
eat1();
return { name: jsonPath.join("."), jsonPath, op: "<truthy>", value: null, caseSensitive: false };
}
const operator = readOperator();
let value = void 0;
let caseSensitive = true;
skipSpaces();
if (next() === "/") {
if (operator !== "=")
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`);
value = readRegularExpression();
} else if (next() === `'` || next() === `"`) {
value = readQuotedString(next()).slice(1, -1);
skipSpaces();
if (next() === "i" || next() === "I") {
caseSensitive = false;
eat1();
} else if (next() === "s" || next() === "S") {
caseSensitive = true;
eat1();
}
} else {
value = "";
while (!EOL && (isCSSNameChar(next()) || next() === "+" || next() === "."))
value += eat1();
if (value === "true") {
value = true;
} else if (value === "false") {View on GitHub (pinned to 93accf6570)
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
Example fix
// before
const boundary = doc.find('h2').get(0); // an Element — not accepted by adjacentUntil
items.prevUntil(boundary); // GoError: cannot use a '*html.Element' as a selector
// after
const boundary = doc.find('h2'); // keep the Selection
items.prevUntil(boundary);
// or use the selector string directly:
items.prevUntil('h2'); Defensive patterns
Strategy: type-guard
Validate before calling
const isUntilArg = (a) =>
a == null || // until the end
typeof a === 'string' || // selector
(typeof a === 'object' && typeof a.find === 'function'); // Selection — Element is NOT valid
function safeNextUntil(sel, boundary) {
if (!isUntilArg(boundary)) throw new Error(`invalid until-argument: ${typeof boundary}`);
return sel.nextUntil(boundary);
} Type guard
function isUntilBoundary(a) {
if (a == null) return true;
if (typeof a === 'string') return true;
return a && typeof a === 'object' && typeof a.find === 'function' && typeof a.size === 'number';
} Try / catch
try {
sel.nextUntil(arg);
} catch (e) {
if (/cannot use a '.*' as a selector/.test(String(e.message))) {
sel.nextUntil(); // unbounded traversal as fallback
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Error while parsing selector `${selector}`: ${e.message}
- Unexpected end of selector while parsing selector `${selecto
- the argument to each() must be a function
- the argument to filter() must be a function, a selector or a
- the argument to is() must be a function, a selector or a sel
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/1a9c9786a36ad5e9.
Report an issue: GitHub.