symfony/css-selector · error · ExpressionErrorException
Pseudo-elements are not supported.
Error message
Pseudo-elements are not supported.
What it means
ExpressionErrorException thrown by Translator::cssToXPath when any parsed selector carries a pseudo-element (::before, ::after, ::first-line, ::selection, etc.). css-selector translates selector queries to XPath for node selection; pseudo-elements denote generated content positions that XPath cannot address, so the library refuses the whole expression.
Solutions
- Strip pseudo-elements from the selector before translation ('div::after' -> 'div').
- Catch ExpressionErrorException around cssToXPath and treat pseudo-element selectors as unsupported.
- Read pseudo-element content with a different tool (browser JS, DOM inspection) instead of XPath.
- Split selector lists and skip the pseudo-element-bearing selectors.
Example fix
// before
$xpath = $translator->cssToXPath('div::before');
// after
$xpath = $translator->cssToXPath('div'); // pseudo-elements cannot be selected via XPath Defensive patterns
Strategy: try-catch
Validate before calling
if (preg_match('/::?[a-z-]+(\(|$)/i', $selector)) {
// potential pseudo-element; stricter: match ::before|::after|::first-line|::first-letter|::selection
} Type guard
function hasPseudoElement(string $css): bool {
return (bool) preg_match('/::(before|after|first-line|first-letter|selection|placeholder|marker|backdrop|file-selector-button)\b/i', $css);
} Try / catch
try {
$xpath = $translator->cssToXPath($css);
} catch (ExpressionErrorException $e) {
if (str_contains($e->getMessage(), 'Pseudo-elements are not supported')) {
$xpath = $translator->cssToXPath(trim(explode('::', $css)[0], ' ,')); // strip pseudo-element
}
} Prevention
- Strip or split off pseudo-elements before calling cssToXPath.
- Never feed full stylesheet rule selectors to the translator unfiltered.
- Use browser tooling for ::before/::after content instead of XPath.
- Add a pre-check for the '::' sequence on user input.
When it happens
Trigger: Calling cssToXPath with selectors such as 'div::before', 'a:hover::after', 'p::first-line', including a single selector inside a comma-separated list — any pseudo-element anywhere fails the whole translation.
Common situations: Feeding full stylesheet rules or UI-test selectors into the translator; scraping pipelines where authors select ::before content; upgrading code that previously used selectors with pseudo-elements in a browser context.
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
- "*:nth-of-type()" is not implemented.
- "*:first-of-type" is not implemented.
- "*:last-of-type" is not implemented.
- Node " " not supported.
- Combiner " " not supported.
AI-assisted analysis of symfony/css-selector@08e2905152 (2026-09-14).
Data as JSON: /api/errors/92bd0d11758b80c3.
Report an issue: GitHub.
Appendix: source
Thrown at XPath/Translator.php:98
$parts[] = \sprintf("'%s'", substr($string, 0, $pos));
$parts[] = "\"'\"";
$string = substr($string, $pos + 1);
} else {
$parts[] = "'$string'";
break;
}
}
return \sprintf('concat(%s)', implode(', ', $parts));
}
public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
{
$selectors = $this->parseSelectors($cssExpr);
foreach ($selectors as $index => $selector) {
if (null !== $selector->getPseudoElement()) {
throw new ExpressionErrorException('Pseudo-elements are not supported.');
}
$selectors[$index] = $this->selectorToXPath($selector, $prefix);
}
return implode(' | ', $selectors);
}
public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string
{
return ($prefix ?: '').$this->nodeToXPath($selector);
}
/**
* @return $this
*/
public function registerExtension(Extension\ExtensionInterface $extension): static
{View on GitHub (pinned to 08e2905152)