microsoft/playwright · error · InvalidSelectorError
Error while parsing selector `${selector}` - cannot use ${op
Error message
Error while parsing selector `${selector}` - cannot use ${operator} in attribute with non-string matching value - ${value} What it means
Thrown when a non-= operator (*=, ^=, $=, |=, ~=) is paired with a non-string value (number or boolean). Substring/prefix/suffix operators only make sense for strings, so the parser rejects unquoted numeric/boolean operands. Values parsed as `true`/`false` become booleans; others become numbers via `+value` when unquoted strings are disallowed.
Source
Thrown at packages/isomorphic/selectorParser.ts:448
if (value === 'true') {
value = true;
} else if (value === 'false') {
value = false;
} else {
if (!allowUnquotedStrings) {
value = +value;
if (Number.isNaN(value))
syntaxError('parsing attribute value');
}
}
}
skipSpaces();
if (next() !== ']')
syntaxError('parsing attribute value');
eat1();
if (operator !== '=' && typeof value !== 'string')
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);
return { name: jsonPath.join('.'), jsonPath, op: operator, value, caseSensitive };
}
const result: AttributeSelector = {
name: '',
attributes: [],
};
result.name = readIdentifier();
skipSpaces();
while (next() === '[') {
result.attributes.push(readAttribute());
skipSpaces();
}
if (!EOL)
syntaxError(undefined);
if (!result.name && !result.attributes.length)
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - selector cannot be empty`);
return result;View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Quote the value so it is parsed as a string: `[data-index*="42"]`.
- Use `=` if you want exact numeric/boolean matching.
- Coerce the dynamic value to a string before interpolating: `String(value)`.
Example fix
// before
page.locator(`[data-id*=${dynamicId}]`); // numeric id -> rejected
// after
page.locator(`[data-id*="${String(dynamicId)}"]`); Defensive patterns
Strategy: validation
Validate before calling
function assertSubstringOpValueIsString(sel: string) {
const m = sel.match(/\[([^[\]]+?)([*^$|~])=(true|false|-?\d+(?:\.\d+)?)\]/);
if (m) throw new Error(`Operator ${m[2]}= requires a quoted string, got ${m[3]}`);
}
assertSubstringOpValueIsString(selector); Type guard
function substringOpValueIsQuoted(sel: string): boolean {
return !/\[[^[\]]+?[*^$|~]=(true|false|-?\d)/.test(sel);
} Try / catch
try {
await page.locator(sel).click();
} catch (e) {
if (/cannot use .* in attribute with non-string/.test(e.message)) {
sel = sel.replace(/(\[\w+[*^$|~]=)(-?\d+|true|false)(\])/, '$1"$2"$3');
}
throw e;
} Prevention
- Quote dynamic values interpolated into substring operators.
- Coerce IDs to strings before interpolation.
- Prefer `=` for numeric/boolean exact matches.
When it happens
Trigger: Writing `page.locator('[data-index*=42]')`, `[count^=true]`, or any `[attr OP <number|true|false>]` where OP is a substring-style operator. Only happens when allowUnquotedStrings is false (strict parse path).
Common situations: Treating a numeric attribute as text without quotes; using a boolean keyword (`true`/`false`) with a substring operator; data-driven selectors that interpolate a numeric ID with `*=`.
Related errors
- Unsupported token "${unsupportedToken.toSource()}" while par
- Error while parsing css selector "${selector}". Did you mean
- Malformed selector: ${part.name}=${part.body}
- Unexpected end of selector while parsing selector `${selecto
- Error while parsing selector `${selector}`: ${e.message}
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/21184e318b0bc5cf.
Report an issue: GitHub.