symfony/css-selector · error · SyntaxErrorException
Got immediate child pseudo-element
Error message
Got immediate child pseudo-element ":%s" not at the start of a selector
What it means
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.
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.
Example fix
// before
$parser->parse('div :scope p');
// after
$parser->parse(':scope div p'); // or 'div p' without scoping Defensive patterns
Strategy: validation
Validate before calling
if (preg_match('/\S\s+:scope\b|^\s*[^:]*[^:\s]\s+:scope/', $selector) && !preg_match('/^:scope/', trim($selector)) && !preg_match('/[:,>(+~]\s*:scope/', $selector)) {
// heuristic: :scope not at start and not right after a separator
if (!preg_match('/^:scope\b/', trim($selector)) && !preg_match('/[:,>(+~]\s*:scope\b/', $selector)) {
throw new \InvalidArgumentException(':scope must be at the start of a selector or right after a separator');
}
} Try / catch
try {
$nodes = (new Parser())->parse($selector);
} catch (SyntaxErrorException $e) {
// fall back to a selector without :scope
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Unexpected pseudo-element
- Unclosed/invalid string at
- String not allowed as function argument.
- Got too deeply nested :has().
- Expected , but found.
AI-assisted analysis of symfony/css-selector@08e2905152 (2026-09-14).
Data as JSON: /api/errors/e3e45a9b9c8d88a5.
Report an issue: GitHub.
Appendix: source
Thrown at Parser/Parser.php:282
continue;
}
if (!$stream->getPeek()->isDelimiter(['('])) {
$result = new Node\PseudoNode($result, $identifier);
if ('Pseudo[Element[*]:scope]' === $result->__toString()) {
$used = \count($stream->getUsed());
$prevSeparators = [','];
if ($insideRelativeSelector) {
$prevSeparators = [',', '(', '>', '+', '~'];
}
if (!(2 === $used
|| 3 === $used && $stream->getUsed()[0]->isWhiteSpace()
|| $used >= 3 && $stream->getUsed()[$used - 3]->isDelimiter($prevSeparators)
|| $used >= 4
&& $stream->getUsed()[$used - 3]->isWhiteSpace()
&& $stream->getUsed()[$used - 4]->isDelimiter($prevSeparators)
)) {
throw SyntaxErrorException::notAtTheStartOfASelector('scope');
}
}
continue;
}
$stream->getNext();
$stream->skipWhitespace();
if ('not' === strtolower($identifier)) {
if ($insideNegation) {
throw SyntaxErrorException::nestedNot();
}
[$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true, true);
$next = $stream->getNext();
if (null !== $argumentPseudoElement) {
throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside :not()');View on GitHub (pinned to 08e2905152)