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

  1. Strip pseudo-elements from the selector before translation ('div::after' -> 'div').
  2. Catch ExpressionErrorException around cssToXPath and treat pseudo-element selectors as unsupported.
  3. Read pseudo-element content with a different tool (browser JS, DOM inspection) instead of XPath.
  4. 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

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


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)