symfony/css-selector · error · ExpressionErrorException
"*:nth-of-type()" is not implemented.
Error message
"*:nth-of-type()" is not implemented.
What it means
ExpressionErrorException thrown by FunctionExtension::translateNthLastOfType when the XPath expression's element is '*' (universal selector), i.e. the selector is like *:nth-last-of-type(). Counting by element type relative to the end is impossible when no element type is known, so the library refuses rather than emit wrong XPath.
Solutions
- Replace '*' with the concrete element type, e.g. 'p:nth-last-of-type(2)'.
- Use :nth-last-child() instead, which works with the universal selector.
- Catch ExpressionErrorException and fall back to a DOM/query library that supports this case.
- Filter or rewrite selectors containing '*:nth-of-type'/'*:nth-last-of-type' before translation.
Example fix
// before '*:nth-last-of-type(2)' // after 'p:nth-last-of-type(2)' // or 'p:nth-last-child(2)'
Defensive patterns
Strategy: try-catch
Validate before calling
if (preg_match('/^\*:(nth-last-of-type|nth-of-type)/', $selector)) {
throw new \InvalidArgumentException('Rewrite *:nth-...-of-type with a concrete element type.');
} Type guard
function needsElementTypeForOfType(string $css): bool {
return (bool) preg_match('/(^|[\s,+>~])\*:(nth-)?(last-)?of-type/', $css);
} Try / catch
try {
$xpath = $translator->cssToXPath($css);
} catch (ExpressionErrorException $e) {
if (str_contains($e->getMessage(), 'not implemented')) {
$css = str_replace('*:nth-last-of-type', 'div:nth-last-child', $css);
$xpath = $translator->cssToXPath($css);
}
} Prevention
- Never combine the universal selector with *-of-type pseudo-classes.
- Prefer :nth-last-child/:nth-child when element type is irrelevant.
- Normalize selectors before translation to insert concrete element names.
- Test translated selectors against expected XPath in CI.
When it happens
Trigger: Translating '*:nth-last-of-type(2)' or a compound where the type was erased to universal, e.g. '*:nth-last-of-type(1n)', through Translator::cssToXPath.
Common situations: Writing :nth-last-of-type() without a preceding element/type selector; automated selector generation that drops the element name; confusion between :nth-last-child (works on *) and :nth-last-of-type (does not).
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
- "*:first-of-type" is not implemented.
- "*:last-of-type" is not implemented.
- Pseudo-class " " not supported.
- Pseudo-elements are not supported.
- Node " " not supported.
AI-assisted analysis of symfony/css-selector@08e2905152 (2026-09-14).
Data as JSON: /api/errors/03a2f4611043dcbd.
Report an issue: GitHub.
Appendix: source
Thrown at XPath/Extension/FunctionExtension.php:119
}
public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
return $this->translateNthChild($xpath, $function, true);
}
public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
return $this->translateNthChild($xpath, $function, false, false);
}
/**
* @throws ExpressionErrorException
*/
public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
if ('*' === $xpath->getElement()) {
throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
}
return $this->translateNthChild($xpath, $function, true, false);
}
/**
* @throws ExpressionErrorException
*/
public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
$arguments = $function->getArguments();
foreach ($arguments as $token) {
if (!($token->isString() || $token->isIdentifier())) {
throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
}
}
return $xpath->addCondition(\sprintf(View on GitHub (pinned to 08e2905152)