symfony/css-selector · error · ExpressionErrorException

"*:last-of-type" is not implemented.

Error message

"*:last-of-type" is not implemented.

What it means

ExpressionErrorException thrown by PseudoClassExtension::translateLastOfType when the XPath element is '*'. Like :first-of-type, 'last element of its type' cannot be expressed against a universal selector, so '*:last-of-type' is rejected outright.

Solutions

  1. Replace '*' with a concrete element type: 'tr:last-of-type'.
  2. Use ':last-child' when any element type is acceptable.
  3. Catch ExpressionErrorException and handle the selector at the application level.
  4. Pre-process selectors to detect '*:last-of-type' before calling cssToXPath.

Example fix

// before
'*:last-of-type'
// after
'tr:last-of-type' // or '*:last-child'
Defensive patterns

Strategy: try-catch

Validate before calling

if (preg_match('/^\*:last-of-type$/', trim($selector))) {
    throw new \InvalidArgumentException('Provide a concrete element type with :last-of-type.');
}

Type guard

function universalLastOfType(string $css): bool {
    return (bool) preg_match('/(^|[\s,+>~])\*:last-of-type/', $css);
}

Try / catch

try {
    $xpath = $translator->cssToXPath($css);
} catch (ExpressionErrorException $e) {
    if (str_contains($e->getMessage(), 'last-of-type')) {
        $xpath = $translator->cssToXPath(str_replace('*:last-of-type', '*:last-child', $css));
    }
}

Prevention

When it happens

Trigger: Translating '*:last-of-type' or selectors normalized so the element is '*' with :last-of-type attached, via Translator::cssToXPath.

Common situations: Same confusion as with :first-of-type/:nth-last-of-type — using of-type pseudo-classes without a type selector; auto-generated or minified selectors losing the element name.

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/13f257d0d8d1d454. Report an issue: GitHub.

Appendix: source

Thrown at XPath/Extension/PseudoClassExtension.php:90

     */
    public function translateFirstOfType(XPathExpr $xpath): XPathExpr
    {
        if ('*' === $xpath->getElement()) {
            throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
        }

        return $xpath
            ->addStarPrefix()
            ->addCondition('position() = 1');
    }

    /**
     * @throws ExpressionErrorException
     */
    public function translateLastOfType(XPathExpr $xpath): XPathExpr
    {
        if ('*' === $xpath->getElement()) {
            throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
        }

        return $xpath
            ->addStarPrefix()
            ->addCondition('position() = last()');
    }

    public function translateOnlyChild(XPathExpr $xpath): XPathExpr
    {
        return $xpath
            ->addStarPrefix()
            ->addNameTest()
            ->addCondition('last() = 1');
    }

    public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
    {
        $element = $xpath->getElement();

View on GitHub (pinned to 08e2905152)