twigphp/Twig · error · LogicException

getTokenParsers() must return an array of…

Error message

getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.

What it means

During extension initialization Twig iterates each extension's getTokenParsers() and validates that every element implements \Twig\TokenParser\TokenParserInterface. This LogicException means a returned element is not a valid token parser — usually a class-name string, a non-parser object, or a wrong getter returning the wrong collection.

Solutions

  1. Make getTokenParsers() return a flat array of instances: return [new MyTagParser()]; not [MyTagParser::class].
  2. Verify each element passes `instanceof \Twig\TokenParser\TokenParserInterface` and filter/assert before returning.
  3. Check that filters come from getFilters(), tests from getTests(), and only token parsers from getTokenParsers().
  4. Upgrade or replace the incompatible third-party extension for your Twig major version.

Example fix

// before
class MyExtension extends AbstractExtension {
    public function getTokenParsers(): array { return [MyTagParser::class]; } // string, not instance
}

// after
class MyExtension extends AbstractExtension {
    public function getTokenParsers(): array { return [new MyTagParser()]; }
}
Defensive patterns

Strategy: validation

Validate before calling

// Self-check an extension before registering:
foreach ($ext->getTokenParsers() as $p) {
    if (!$p instanceof \Twig\TokenParser\TokenParserInterface) {
        throw new \TypeError(get_class($ext).'::getTokenParsers() returned '.get_debug_type($p));
    }
}

Type guard

function isValidTokenParser(mixed $p): bool {
    return $p instanceof \Twig\TokenParser\TokenParserInterface;
}

Try / catch

try {
    $twig->addExtension($ext);
    $twig->loadTemplate('any.twig'); // triggers initExtensions()
} catch (\LogicException $e) {
    if (str_contains($e->getMessage(), 'getTokenParsers()')) {
        // fix or replace the broken extension's getTokenParsers()
    }
    throw $e;
}

Prevention

When it happens

Trigger: An extension's getTokenParsers() returns elements that are not TokenParserInterface instances — returning class-name strings, returning null entries, mixing filters/tests into getTokenParsers(), or a mis-typed collection in a custom or third-party extension. Fires when initExtensions() runs (first template load/render after adding the extension).

Common situations: Upgrading Twig across major versions where an old extension returned parser class names; copy-pasted extension code returning the wrong collection; hand-written extensions returning new MyParser() wrapped in an extra array level or with typos.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/9add8180c0a6d761. Report an issue: GitHub.

Appendix: source

Thrown at src/ExtensionSet.php:491

        foreach ($extension->getFunctions() as $function) {
            $this->functions[$name = $function->getName()] = $function;
            if (str_contains($name, '*')) {
                $this->dynamicFunctions['#^'.str_replace('\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $function;
            }
        }

        // tests
        foreach ($extension->getTests() as $test) {
            $this->tests[$name = $test->getName()] = $test;
            if (str_contains($name, '*')) {
                $this->dynamicTests['#^'.str_replace('\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $test;
            }
        }

        // token parsers
        foreach ($extension->getTokenParsers() as $parser) {
            if (!$parser instanceof TokenParserInterface) {
                throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.');
            }

            $this->parsers[$parser->getTag()] = $parser;
        }

        // node visitors
        foreach ($extension->getNodeVisitors() as $visitor) {
            $this->visitors[] = $visitor;
        }

        // expression parsers
        if (method_exists($extension, 'getExpressionParsers')) {
            $this->expressionParsers->add($extension->getExpressionParsers());
        }

        $operators = $extension->getOperators();
        if (!\is_array($operators)) {
            throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array with operators, got "%s".', $extension::class, get_debug_type($operators).(\is_resource($operators) ? '' : '#'.$operators)));

View on GitHub (pinned to a414c3a491)