{"record":{"id":"e3e45a9b9c8d88a5","repo":"symfony/css-selector","slug":"got-immediate-child-pseudo-element-s-not-at-the-start-of-a","errorCode":null,"errorMessage":"Got immediate child pseudo-element \":%s\" not at the start of a selector","messagePattern":"Got immediate child pseudo-element \":(.+?)\" not at the start of a selector","errorType":"exception","errorClass":"SyntaxErrorException","httpStatus":null,"severity":"error","filePath":"Parser/Parser.php","lineNumber":282,"sourceCode":"                    continue;\n                }\n\n                if (!$stream->getPeek()->isDelimiter(['('])) {\n                    $result = new Node\\PseudoNode($result, $identifier);\n                    if ('Pseudo[Element[*]:scope]' === $result->__toString()) {\n                        $used = \\count($stream->getUsed());\n                        $prevSeparators = [','];\n                        if ($insideRelativeSelector) {\n                            $prevSeparators = [',', '(', '>', '+', '~'];\n                        }\n                        if (!(2 === $used\n                           || 3 === $used && $stream->getUsed()[0]->isWhiteSpace()\n                           || $used >= 3 && $stream->getUsed()[$used - 3]->isDelimiter($prevSeparators)\n                           || $used >= 4\n                                && $stream->getUsed()[$used - 3]->isWhiteSpace()\n                                && $stream->getUsed()[$used - 4]->isDelimiter($prevSeparators)\n                        )) {\n                            throw SyntaxErrorException::notAtTheStartOfASelector('scope');\n                        }\n                    }\n                    continue;\n                }\n\n                $stream->getNext();\n                $stream->skipWhitespace();\n\n                if ('not' === strtolower($identifier)) {\n                    if ($insideNegation) {\n                        throw SyntaxErrorException::nestedNot();\n                    }\n\n                    [$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true, true);\n                    $next = $stream->getNext();\n\n                    if (null !== $argumentPseudoElement) {\n                        throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside :not()');","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/symfony/css-selector/blob/08e2905152a39cf3fd1745d83f8c483e258887d9/Parser/Parser.php#L264-L300","documentation":"SyntaxErrorException thrown by Parser::parseSimpleSelector() when the :scope pseudo-class appears anywhere other than the start of a selector (or immediately after a separator like ',', '(', '>', '+', '~'). CSS defines :scope as anchoring to the scoping root, so mid-selector occurrences like 'div :scope p' are invalid in this parser.","triggerScenarios":"Parsing selectors where ':scope' follows another selector or descendant combinator at start-of-selector position, e.g. 'div :scope', '.a :scope.b'. The check at Parser/Parser.php:270-283 inspects the used-token stream (separators ',' for normal, plus '(', '>', '+', '~' inside relative selectors) and throws when :scope is not preceded by an allowed separator at the start.","commonSituations":"Rewriting selectors to scope them with :scope by naive concatenation ('oldSelector + \" :scope ...\"'), porting browser querySelector code that allows relative :scope, generating scoped queries inside :has()/:is() arguments.","solutions":["Place :scope at the very start of the selector: ':scope > div' instead of 'div :scope'.","If you need to scope relative to a context element, prepend ':scope ' once at the top level and keep inner selectors :scope-free.","When building selectors programmatically, only ever prefix ':scope' to the final string, never inject it mid-string.","Catch SyntaxErrorException and fall back to a :scope-free selector."],"exampleFix":"// before\n$parser->parse('div :scope p');\n\n// after\n$parser->parse(':scope div p'); // or 'div p' without scoping","handlingStrategy":"validation","validationCode":"if (preg_match('/\\S\\s+:scope\\b|^\\s*[^:]*[^:\\s]\\s+:scope/', $selector) && !preg_match('/^:scope/', trim($selector)) && !preg_match('/[:,>(+~]\\s*:scope/', $selector)) {\n    // heuristic: :scope not at start and not right after a separator\n    if (!preg_match('/^:scope\\b/', trim($selector)) && !preg_match('/[:,>(+~]\\s*:scope\\b/', $selector)) {\n        throw new \\InvalidArgumentException(':scope must be at the start of a selector or right after a separator');\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $nodes = (new Parser())->parse($selector);\n} catch (SyntaxErrorException $e) {\n    // fall back to a selector without :scope\n}","preventionTips":["Only ever prefix ':scope' at the beginning of a selector string","Never inject :scope via string concatenation into the middle of a selector","Use descendant combinators instead of mid-selector :scope"],"tags":["css-selector","parser","scope-pseudo-class","syntax-error","php"],"backgroundTag":"invalid-argument-value","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"}