symfony/css-selector · error · SyntaxErrorException
Got nested :not().
Error message
Got nested :not().
What it means
SyntaxErrorException thrown by Parser::parseSimpleSelector() when a :not() appears inside another :not(). The parser passes $insideNegation=true into the nested parseSimpleSelector and rejects ':not(:not(...))' because nested negation is not allowed per the CSS Selectors level 3/4 rules this component implements.
Solutions
- Remove the inner :not(): ':not(.a)' instead of ':not(:not(.a))' (double negation is identity).
- Use :is() to express matching: ':is(.a)' for positive matching.
- Before wrapping a selector in :not(), check whether it already starts with :not( and unwrap instead.
- Catch SyntaxErrorException for untrusted selector input.
Example fix
// before
$parser->parse('div:not(:not(.active))');
// after
$parser->parse('div:not(.active)'); Defensive patterns
Strategy: validation
Validate before calling
if (preg_match('/:not\(\s*:not\(/i', $selector)) {
throw new \InvalidArgumentException('Nested :not() is not supported; use the inner condition directly');
} Try / catch
try {
$nodes = (new Parser())->parse($selector);
} catch (SyntaxErrorException $e) {
if (str_contains($e->getMessage(), ':not()')) { /* simplify selector */ }
} Prevention
- Unwrap existing :not() before wrapping a selector in another :not()
- Remember double negation is identity — simplify instead
- Use :is() for positive matching
When it happens
Trigger: Parsing selectors like 'div:not(:not(.a))', ':not(:not(#id))'. Fired at Parser/Parser.php:291-294 when identifier is 'not' and $insideNegation is already true (i.e. we are parsing the argument of an outer :not).
Common situations: Programmatic negation of already-negated selectors (wrapping user selectors in :not() blindly), minified/generated CSS containing double negation, attempts to express 'matches' via double-negation instead of :is().
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Unexpected pseudo-element
- Got too deeply nested :has().
- Expected , but found.
- Got immediate child pseudo-element
- Got too deeply nested
AI-assisted analysis of symfony/css-selector@08e2905152 (2026-09-14).
Data as JSON: /api/errors/d28f69632a7ecbde.
Report an issue: GitHub.
Appendix: source
Thrown at Parser/Parser.php:293
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()');
}
if (!$next->isDelimiter([')'])) {
throw SyntaxErrorException::unexpectedToken('")"', $next);
}
$result = new Node\NegationNode($result, $argument);
} elseif ('is' === strtolower($identifier)) {
$selectors = $this->parseNestedSelectorList($stream, 'is');
$next = $stream->getNext();View on GitHub (pinned to 08e2905152)