{"record":{"id":"92bd0d11758b80c3","repo":"symfony/css-selector","slug":"pseudo-elements-are-not-supported","errorCode":null,"errorMessage":"Pseudo-elements are not supported.","messagePattern":"Pseudo-elements are not supported\\.","errorType":"exception","errorClass":"ExpressionErrorException","httpStatus":null,"severity":"error","filePath":"XPath/Translator.php","lineNumber":98,"sourceCode":"                $parts[] = \\sprintf(\"'%s'\", substr($string, 0, $pos));\n                $parts[] = \"\\\"'\\\"\";\n                $string = substr($string, $pos + 1);\n            } else {\n                $parts[] = \"'$string'\";\n                break;\n            }\n        }\n\n        return \\sprintf('concat(%s)', implode(', ', $parts));\n    }\n\n    public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string\n    {\n        $selectors = $this->parseSelectors($cssExpr);\n\n        foreach ($selectors as $index => $selector) {\n            if (null !== $selector->getPseudoElement()) {\n                throw new ExpressionErrorException('Pseudo-elements are not supported.');\n            }\n\n            $selectors[$index] = $this->selectorToXPath($selector, $prefix);\n        }\n\n        return implode(' | ', $selectors);\n    }\n\n    public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string\n    {\n        return ($prefix ?: '').$this->nodeToXPath($selector);\n    }\n\n    /**\n     * @return $this\n     */\n    public function registerExtension(Extension\\ExtensionInterface $extension): static\n    {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/symfony/css-selector/blob/08e2905152a39cf3fd1745d83f8c483e258887d9/XPath/Translator.php#L80-L116","documentation":"ExpressionErrorException thrown by Translator::cssToXPath when any parsed selector carries a pseudo-element (::before, ::after, ::first-line, ::selection, etc.). css-selector translates selector queries to XPath for node selection; pseudo-elements denote generated content positions that XPath cannot address, so the library refuses the whole expression.","triggerScenarios":"Calling cssToXPath with selectors such as 'div::before', 'a:hover::after', 'p::first-line', including a single selector inside a comma-separated list — any pseudo-element anywhere fails the whole translation.","commonSituations":"Feeding full stylesheet rules or UI-test selectors into the translator; scraping pipelines where authors select ::before content; upgrading code that previously used selectors with pseudo-elements in a browser context.","solutions":["Strip pseudo-elements from the selector before translation ('div::after' -> 'div').","Catch ExpressionErrorException around cssToXPath and treat pseudo-element selectors as unsupported.","Read pseudo-element content with a different tool (browser JS, DOM inspection) instead of XPath.","Split selector lists and skip the pseudo-element-bearing selectors."],"exampleFix":"// before\n$xpath = $translator->cssToXPath('div::before');\n// after\n$xpath = $translator->cssToXPath('div'); // pseudo-elements cannot be selected via XPath","handlingStrategy":"try-catch","validationCode":"if (preg_match('/::?[a-z-]+(\\(|$)/i', $selector)) {\n    // potential pseudo-element; stricter: match ::before|::after|::first-line|::first-letter|::selection\n}","typeGuard":"function hasPseudoElement(string $css): bool {\n    return (bool) preg_match('/::(before|after|first-line|first-letter|selection|placeholder|marker|backdrop|file-selector-button)\\b/i', $css);\n}","tryCatchPattern":"try {\n    $xpath = $translator->cssToXPath($css);\n} catch (ExpressionErrorException $e) {\n    if (str_contains($e->getMessage(), 'Pseudo-elements are not supported')) {\n        $xpath = $translator->cssToXPath(trim(explode('::', $css)[0], ' ,')); // strip pseudo-element\n    }\n}","preventionTips":["Strip or split off pseudo-elements before calling cssToXPath.","Never feed full stylesheet rule selectors to the translator unfiltered.","Use browser tooling for ::before/::after content instead of XPath.","Add a pre-check for the '::' sequence on user input."],"tags":["css-selector","pseudo-element","xpath"],"backgroundTag":"unsupported-operation","analyzedSha":"08e2905152a39cf3fd1745d83f8c483e258887d9","analyzedAt":"2026-09-14T11:41:28.509Z","contentChangedAt":"2026-09-14T11:41:28.509Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}