{"record":{"id":"e456513d2fed950d","repo":"microsoft/playwright","slug":"error-while-parsing-css-selector-selector-di","errorCode":null,"errorMessage":"Error while parsing css selector \"${selector}\". Did you mean to CSS.escape it?","messagePattern":"Error while parsing css selector \"(.+?)\"\\. Did you mean to CSS\\.escape it\\?","errorType":"exception","errorClass":"InvalidSelectorError","httpStatus":null,"severity":"error","filePath":"packages/isomorphic/cssParser.ts","lineNumber":249,"sourceCode":"    let s = '';\n    let balance = 1;  // First open paren is a part of a function token.\n    while (!isEOF()) {\n      if (isOpenParen() || isFunction())\n        balance++;\n      if (isCloseParen())\n        balance--;\n      if (!balance)\n        break;\n      s += tokens[pos++].toSource();\n    }\n    return s;\n  }\n\n  const result = consumeFunctionArguments();\n  if (!isEOF())\n    throw unexpected();\n  if (result.some(arg => typeof arg !== 'object' || !('simples' in arg)))\n    throw new InvalidSelectorError(`Error while parsing css selector \"${selector}\". Did you mean to CSS.escape it?`);\n  return { selector: result as CSSComplexSelector[], names: Array.from(names) };\n}\n\nexport function serializeSelector(args: CSSFunctionArgument[]) {\n  return args.map(arg => {\n    if (typeof arg === 'string')\n      return `\"${arg}\"`;\n    if (typeof arg === 'number')\n      return String(arg);\n    return arg.simples.map(({ selector, combinator }) => {\n      let s = selector.css || '';\n      s = s + selector.functions.map(func => `:${func.name}(${serializeSelector(func.args)})`).join('');\n      if (combinator)\n        s += ' ' + combinator;\n      return s;\n    }).join(' ');\n  }).join(', ');\n}","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/isomorphic/cssParser.ts#L231-L267","documentation":"Thrown at the end of parseCSS after function arguments are consumed. Every argument of a pseudo-class function (like :has(), :is(), :not()) is expected to be a CSSComplexSelector — an object containing a 'simples' array. If any argument is instead a bare string or number (i.e. the function was called with a non-selector argument shape), the structural check fails and this error is raised.","triggerScenarios":"Using a Playwright custom pseudo-class with a non-selector argument, e.g. :has-text() is fine (string) but :has(123) or :is(\"str\") where a complex selector is required; malformed nested selectors where the closing of an inner complex selector is missing so the parser falls back to treating the arg as a primitive.","commonSituations":"Hand-writing selectors with custom engines (:not(...), :has(...), :is(...), :where(...)) and putting a literal where a sub-selector goes; refactoring a selector and dropping a nested combinator; using a string where the grammar requires brackets/selector syntax.","solutions":["Inspect the selector in the message: each function argument that is a selector must be a valid selector expression, not a literal.","Replace literal arguments with proper sub-selectors, e.g. change :has(foo) so 'foo' is itself a valid selector.","If you need text matching use the dedicated engines: :has-text(\"...\"), :text(\"...\"), :text-is(\"...\").","Run the selector through page.locator() in isolation to reproduce, then simplify until it parses."],"exampleFix":"// before\nawait page.locator(':has(div)').click(); // missing base + arg shape\n\n// after\nawait page.locator('div:has(span)').click(); // base 'div' + valid sub-selector 'span'","handlingStrategy":"try-catch","validationCode":"function looksLikeValidFunctionArg(sel: string): boolean {\n  // crude: pseudo-class function args that are selectors should not be bare literals\n  return !/^\\s*\"|\\d+\\s*$/.test(sel);\n}","typeGuard":null,"tryCatchPattern":"try { await page.locator(sel).click(); }\ncatch (e) { if (isInvalidSelectorError(e)) handleBadSelector(sel); else throw e; }","preventionTips":["Use Playwright's high-level pseudo-class APIs (:has-text, has, filter) instead of hand-writing function args.","Reproduce suspect selectors in isolation before composing them into larger expressions.","Keep nested pseudo-class arguments as valid sub-selectors, never literals."],"tags":["css","selector","parsing","invalid-selector","pseudo-class"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}