{"record":{"id":"d28f69632a7ecbde","repo":"symfony/css-selector","slug":"got-nested-not","errorCode":null,"errorMessage":"Got nested :not().","messagePattern":"Got nested :not\\(\\)\\.","errorType":"exception","errorClass":"SyntaxErrorException","httpStatus":null,"severity":"error","filePath":"Parser/Parser.php","lineNumber":293,"sourceCode":"                        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()');\n                    }\n\n                    if (!$next->isDelimiter([')'])) {\n                        throw SyntaxErrorException::unexpectedToken('\")\"', $next);\n                    }\n\n                    $result = new Node\\NegationNode($result, $argument);\n                } elseif ('is' === strtolower($identifier)) {\n                    $selectors = $this->parseNestedSelectorList($stream, 'is');\n\n                    $next = $stream->getNext();","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/symfony/css-selector/blob/08e2905152a39cf3fd1745d83f8c483e258887d9/Parser/Parser.php#L275-L311","documentation":"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.","triggerScenarios":"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).","commonSituations":"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().","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."],"exampleFix":"// before\n$parser->parse('div:not(:not(.active))');\n\n// after\n$parser->parse('div:not(.active)');","handlingStrategy":"validation","validationCode":"if (preg_match('/:not\\(\\s*:not\\(/i', $selector)) {\n    throw new \\InvalidArgumentException('Nested :not() is not supported; use the inner condition directly');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $nodes = (new Parser())->parse($selector);\n} catch (SyntaxErrorException $e) {\n    if (str_contains($e->getMessage(), ':not()')) { /* simplify selector */ }\n}","preventionTips":["Unwrap existing :not() before wrapping a selector in another :not()","Remember double negation is identity — simplify instead","Use :is() for positive matching"],"tags":["css-selector","parser","not-pseudo-class","nested-negation","php"],"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"}