microsoft/playwright · error · InvalidSelectorError
Unexpected end of selector while parsing selector `${selecto
Error message
Unexpected end of selector while parsing selector `${selector}` What it means
Thrown by parseAttributeSelector when the parser hits end-of-string in the middle of a sub-parse stage (quoted string, regex, identifier, operator, or attribute value). It is an InvalidSelectorError indicating the selector text is truncated or unterminated. The parser expected more characters but ran out of input before completing the current token.
Source
Thrown at packages/isomorphic/selectorParser.ts:286
attributes: AttributeSelectorPart[],
};
export function parseAttributeSelector(selector: string, allowUnquotedStrings: boolean): AttributeSelector {
let wp = 0;
let EOL = selector.length === 0;
const next = () => selector[wp] || '';
const eat1 = () => {
const result = next();
++wp;
EOL = wp >= selector.length;
return result;
};
const syntaxError = (stage: string|undefined) => {
if (EOL)
throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \`${selector}\``);
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? ' during ' + stage : ''));
};
function skipSpaces() {
while (!EOL && /\s/.test(next()))
eat1();
}
function isCSSNameChar(char: string) {
// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
return (char >= '\u0080') // non-ascii
|| (char >= '\u0030' && char <= '\u0039') // digit
|| (char >= '\u0041' && char <= '\u005a') // uppercase letter
|| (char >= '\u0061' && char <= '\u007a') // lowercase letter
|| (char >= '\u0030' && char <= '\u0039') // digit
|| char === '\u005f' // "_"
|| char === '\u002d'; // "-"
}View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Inspect the full selector string printed in the message and add the missing closing character (quote, ], =value).
- If the selector is built dynamically, log it before the call and assert it is non-empty and well-formed.
- Quote attribute values that may be empty or contain spaces: use "value" or 'value' inside [...].
- Switch to a literal selector in tests to isolate whether the bug is in the dynamic value or the static structure.
Example fix
// before
const sel = `button[data-test=${id}`; // missing closing ] when id undefined/truncated
page.locator(sel);
// after
const sel = `button[data-test="${id}"]`;
if (!sel.includes('[]') && !sel.includes('=""'))
throw new Error(`bad selector: ${sel}`);
page.locator(sel); Defensive patterns
Strategy: validation
Validate before calling
function assertClosedSelector(sel: string) {
const open = (sel.match(/[\['"]/g) ?? []).length;
const close = (sel.match(/[\]'"]/g) ?? []).length;
if (open !== close) throw new Error(`Selector brackets/quotes unbalanced: ${sel}`);
}
assertClosedSelector(selector); Type guard
function isValidSelectorStart(sel: string): boolean {
return typeof sel === 'string' && sel.trim().length > 0;
} Try / catch
import { InvalidSelectorError } from 'playwright-core';
try {
await page.locator(sel).click();
} catch (e) {
if (e instanceof InvalidSelectorError || /Unexpected end of selector/.test(e.message)) {
logSelectorBuildFailure(sel, e);
throw new Error(`Refusing to act on malformed selector: ${sel}`);
}
throw e;
} Prevention
- Always quote attribute values to avoid EOL mid-token.
- Build selectors from small, individually-validated fragments and unit-test the builder.
- Log the assembled selector string before using it in CI to catch truncation.
When it happens
Trigger: Calling locator/query APIs with a truncated attribute selector such as `page.locator('button[text=')`, `page.locator('[aria-label')`, or `parseAttributeSelector('div[name="unterminated', false)`. Any input where eat1()/next() reach EOL while skipSpaces/readQuotedString/readOperator/readAttributeToken still expect a closing token.
Common situations: String concatenation that drops a closing bracket or quote; template literals with an undefined variable producing empty fragments; copy-paste of a CSS selector into a Playwright attribute-engine selector expecting quoted values; CI where a selector is built from config and a field is missing.
Related errors
- Unsupported token "${unsupportedToken.toSource()}" while par
- Error while parsing css selector "${selector}". Did you mean
- Malformed selector: ${part.name}=${part.body}
- Error while parsing selector `${selector}`: ${e.message}
- Error while parsing selector `${selector}` - cannot use ${op
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/3d3632bbf129b588.
Report an issue: GitHub.