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 regular expression

What it means

Thrown when an attribute selector uses a regex value (`/.../`) with an operator other than `=`. Regex matching only supports the plain equality operator; substring/prefix/suffix operators (*=, ^=, $=, |=, ~=) are incompatible with regex semantics and are rejected.

Source

Thrown at packages/isomorphic/selectorParser.ts:414

    while (next() === '.') {
      eat1();
      jsonPath.push(readAttributeToken());
      skipSpaces();
    }
    // check property is truthy: [enabled]
    if (next() === ']') {
      eat1();
      return { name: jsonPath.join('.'), jsonPath, op: '<truthy>', value: null, caseSensitive: false };
    }

    const operator = readOperator();

    let value = undefined;
    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 c8fc3bf8d3)

Solutions

  1. Replace the substring/prefix/suffix operator with `=` and fold the matching intent into the regex, e.g. `[class=/.*active.*/]`.
  2. If you need a prefix match, anchor the regex: `[href=/^https/]`.
  3. Drop the regex and use the plain operator with a literal string instead.

Example fix

// before
page.locator('[class*=/active/]');

// after
page.locator('[class=/.*active.*/]');
Defensive patterns

Strategy: validation

Validate before calling

function assertNoRegexWithSubstringOp(sel: string) {
  if (/[\^$|~*]=\/.*\//.test(sel))
    throw new Error('Regex attribute values may only use the = operator');
}
assertNoRegexWithSubstringOp(selector);

Type guard

function regexUsesEqualsOnly(sel: string): boolean {
  return !/(\*|\^|\$|\||~)=\/.*\//.test(sel);
}

Try / catch

try {
  await page.locator(sel).click();
} catch (e) {
  if (/cannot use .* in attribute with regular expression/.test(e.message)) {
    sel = sel.replace(/([*^$|~])=(\/.*\/)/, '=$2');
    // retry once with corrected operator
  }
  throw e;
}

Prevention

When it happens

Trigger: Writing `page.locator('[class*=/active/]')`, `[href^=/https/]`, `[data-id$=/\d+/]`, or any `[attr OP /regex/]` where OP is not `=`.

Common situations: Mistakenly combining CSS substring-match operators with Playwright's regex extension; assuming `*=` works like `.*` with a regex; refactor that changed `=` to `*=` without removing the slashes.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/95f2fa69e4a4d4d1. Report an issue: GitHub.