{"record":{"id":"1b082ba0763452d3","repo":"symfony/css-selector","slug":"unexpected-pseudo-element-s-found-s","errorCode":null,"errorMessage":"Unexpected pseudo-element \"::%s\" found %s.","messagePattern":"Unexpected pseudo-element \"::(.+?)\" found (.+?)\\.","errorType":"exception","errorClass":"SyntaxErrorException","httpStatus":null,"severity":"error","filePath":"Parser/Parser.php","lineNumber":136,"sourceCode":"\n    private function parserSelectorNode(TokenStream $stream, bool $isArgument = false, bool $insideRelativeSelector = false): Node\\SelectorNode\n    {\n        [$result, $pseudoElement] = $this->parseSimpleSelector($stream, false, $isArgument, $insideRelativeSelector);\n\n        while (true) {\n            $stream->skipWhitespace();\n            $peek = $stream->getPeek();\n\n            if (\n                $peek->isFileEnd()\n                || $peek->isDelimiter([','])\n                || ($isArgument && $peek->isDelimiter([')']))\n            ) {\n                break;\n            }\n\n            if (null !== $pseudoElement) {\n                throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');\n            }\n\n            if ($peek->isDelimiter(['+', '>', '~'])) {\n                $combinator = $stream->getNext()->getValue();\n                $stream->skipWhitespace();\n            } else {\n                $combinator = ' ';\n            }\n\n            [$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream, false, $isArgument, $insideRelativeSelector);\n            $result = new Node\\CombinedSelectorNode($result, $combinator, $nextSelector);\n        }\n\n        return new Node\\SelectorNode($result, $pseudoElement);\n    }\n\n    /**\n     * @return list<array{0: string, 1: Node\\SelectorNode}>","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/symfony/css-selector/blob/08e2905152a39cf3fd1745d83f8c483e258887d9/Parser/Parser.php#L118-L154","documentation":"SyntaxErrorException thrown by Parser::parserSelectorNode() when a pseudo-element (e.g. ::before, ::after) appears anywhere other than as the final component of a compound selector chain. CSS grammar only allows a pseudo-element at the very end of a selector, so the parser rejects any selector where more components (combinators, classes, attributes) follow a pseudo-element.","triggerScenarios":"Calling Parser::parse() (or parseSelectorList via parse) with a selector where a pseudo-element is followed by another selector part, e.g. 'div::before span', 'a::first-line.class', 'ul ::after > li'. The check at Parser/Parser.php:135-137 fires when $pseudoElement is already set and the loop continues past end-of-selector tokens (',', EOF, ')').","commonSituations":"Typo or misreading of CSS spec: putting pseudo-elements mid-selector such as '.menu::before .item', concatenating user-supplied selector strings, porting selectors that browsers leniently accept, or generating selectors programmatically with the pseudo-element inserted before combinators.","solutions":["Move the pseudo-element to the end of the full selector: 'div span::before' instead of 'div::before span'.","If you need to style the pseudo-element of an inner element, split into separate selectors per target element.","Validate user-supplied selectors (regex or a pre-parse check for '::' followed by more selector tokens) before passing to Parser::parse().","Catch Symfony\\Component\\CssSelector\\Exception\\SyntaxErrorException and surface a clear message to the caller."],"exampleFix":"// before\n$cssSelector->parse('div::before span');\n\n// after\n$cssSelector->parse('div span::before');","handlingStrategy":"validation","validationCode":"if (preg_match('/::[a-zA-Z-]+\\s*[-.#\\[>+~:]/', $selector) || preg_match('/:(before|after|first-line|first-letter)\\s*[-.#\\[>+~]/i', $selector)) {\n    throw new \\InvalidArgumentException('Pseudo-element must be at the end of the selector: ' . $selector);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $nodes = (new Parser())->parse($selector);\n} catch (SyntaxErrorException $e) {\n    // log and skip this selector\n}","preventionTips":["Always place pseudo-elements last in generated selectors","Validate user-supplied selectors with a regex for mid-selector '::' before parsing","Never append classes/attributes after a pseudo-element token"],"tags":["css-selector","parser","pseudo-element","syntax-error","php"],"backgroundTag":"invalid-regex-pattern","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"}