symfony/css-selector · error · ExpressionErrorException
Pseudo-class " " not supported.
Error message
Pseudo-class "%s" not supported.
What it means
Translator::addPseudoClass() resolves a simple pseudo-class name (e.g. 'hover', 'first-child', 'empty') in $this->pseudoClassTranslators and throws ExpressionErrorException when the name is not registered. Static-state libraries only translate pseudo-classes that make sense in an XPath context, so many browser-only pseudo-classes are intentionally absent.
Solutions
- Remove the unsupported pseudo-class from the selector or replace it with a structural equivalent (e.g. :first-child is supported, :hover is not).
- Register a custom pseudo-class translator via Translator::extension().
- Check supported names: hover is registered, but classes like :visited/:target are typically not — consult pseudoClassTranslators keys.
Example fix
// before
$translator->cssToXPath('a:hover'); // pseudo-class not supported, throws
// after
$translator->cssToXPath('a'); // drop interactive pseudo-class, filter matches in code Defensive patterns
Strategy: try-catch
Validate before calling
$supported = ['empty','first-child','last-child','first-of-type','last-of-type','only-child','hover','root','link','checked','disabled','enabled'];
$pseudo = implode('|', $supported);
if (preg_match('/:(' . $pseudo . ')?/)', '') !== false) { /* pre-check pseudo names */ }
if (!preg_match('/^:(?:' . $pseudo . ')$/', ':' . $name)) {
throw new \InvalidArgumentException("Unsupported pseudo-class: $name");
} Type guard
function pseudoClassSupported(string $name): bool {
static $supported = ['first-child','last-child','empty','hover','root','link','checked','disabled','enabled'];
return in_array(ltrim($name, ':'), $supported, true);
} Try / catch
use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
try {
$xpath = $translator->cssToXPath($css);
} catch (ExpressionErrorException $e) {
$css = preg_replace('/:hover|:focus|:active/', '', $css); // drop interactive pseudo-classes
$xpath = $translator->cssToXPath($css);
} Prevention
- Strip browser-only interactive pseudo-classes before translating selectors
- Maintain a project-wide list of allowed pseudo-classes for scraped/derived CSS
- Fail fast in tests when a selector contains untranslatable pseudo-classes
When it happens
Trigger: Calling addPseudoClass() with a $pseudoClass string missing from pseudoClassTranslators — e.g. selector ':hover', ':focus', ':visited' passed to the translator, or a misspelled pseudo-class like ':first-of-typee'.
Common situations: Feeding interactive/browser-only CSS (:hover, :focus, :active) to the XPath converter in scraping or testing code; typos in pseudo-class names; custom pseudo-classes without a registered translator.
Related errors
- "*:nth-of-type()" is not implemented.
- "*:first-of-type" is not implemented.
- "*:last-of-type" is not implemented.
- Pseudo-elements are not supported.
- Node " " not supported.
AI-assisted analysis of symfony/css-selector@08e2905152 (2026-09-14).
Data as JSON: /api/errors/d49da941e92cdb77.
Report an issue: GitHub.
Appendix: source
Thrown at XPath/Translator.php:205
/**
* @throws ExpressionErrorException
*/
public function addFunction(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
if (!isset($this->functionTranslators[$function->getName()])) {
throw new ExpressionErrorException(\sprintf('Function "%s" not supported.', $function->getName()));
}
return $this->functionTranslators[$function->getName()]($xpath, $function);
}
/**
* @throws ExpressionErrorException
*/
public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr
{
if (!isset($this->pseudoClassTranslators[$pseudoClass])) {
throw new ExpressionErrorException(\sprintf('Pseudo-class "%s" not supported.', $pseudoClass));
}
return $this->pseudoClassTranslators[$pseudoClass]($xpath);
}
/**
* @throws ExpressionErrorException
*/
public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, ?string $value): XPathExpr
{
if (!isset($this->attributeMatchingTranslators[$operator])) {
throw new ExpressionErrorException(\sprintf('Attribute matcher operator "%s" not supported.', $operator));
}
return $this->attributeMatchingTranslators[$operator]($xpath, $attribute, $value);
}
/**View on GitHub (pinned to 08e2905152)