{"record":{"id":"9add8180c0a6d761","repo":"twigphp/Twig","slug":"gettokenparsers-must-return-an-array-of-twig-tokenparser","errorCode":null,"errorMessage":"getTokenParsers() must return an array of \\Twig\\TokenParser\\TokenParserInterface.","messagePattern":"getTokenParsers\\(\\) must return an array of \\\\Twig\\\\TokenParser\\\\TokenParserInterface\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/ExtensionSet.php","lineNumber":491,"sourceCode":"        foreach ($extension->getFunctions() as $function) {\n            $this->functions[$name = $function->getName()] = $function;\n            if (str_contains($name, '*')) {\n                $this->dynamicFunctions['#^'.str_replace('\\\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $function;\n            }\n        }\n\n        // tests\n        foreach ($extension->getTests() as $test) {\n            $this->tests[$name = $test->getName()] = $test;\n            if (str_contains($name, '*')) {\n                $this->dynamicTests['#^'.str_replace('\\\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $test;\n            }\n        }\n\n        // token parsers\n        foreach ($extension->getTokenParsers() as $parser) {\n            if (!$parser instanceof TokenParserInterface) {\n                throw new \\LogicException('getTokenParsers() must return an array of \\Twig\\TokenParser\\TokenParserInterface.');\n            }\n\n            $this->parsers[$parser->getTag()] = $parser;\n        }\n\n        // node visitors\n        foreach ($extension->getNodeVisitors() as $visitor) {\n            $this->visitors[] = $visitor;\n        }\n\n        // expression parsers\n        if (method_exists($extension, 'getExpressionParsers')) {\n            $this->expressionParsers->add($extension->getExpressionParsers());\n        }\n\n        $operators = $extension->getOperators();\n        if (!\\is_array($operators)) {\n            throw new \\InvalidArgumentException(\\sprintf('\"%s::getOperators()\" must return an array with operators, got \"%s\".', $extension::class, get_debug_type($operators).(\\is_resource($operators) ? '' : '#'.$operators)));","sourceCodeStart":473,"sourceCodeEnd":509,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/ExtensionSet.php#L473-L509","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Make getTokenParsers() return a flat array of instances: return [new MyTagParser()]; not [MyTagParser::class].","Verify each element passes `instanceof \\Twig\\TokenParser\\TokenParserInterface` and filter/assert before returning.","Check that filters come from getFilters(), tests from getTests(), and only token parsers from getTokenParsers().","Upgrade or replace the incompatible third-party extension for your Twig major version."],"exampleFix":"// before\nclass MyExtension extends AbstractExtension {\n    public function getTokenParsers(): array { return [MyTagParser::class]; } // string, not instance\n}\n\n// after\nclass MyExtension extends AbstractExtension {\n    public function getTokenParsers(): array { return [new MyTagParser()]; }\n}","handlingStrategy":"validation","validationCode":"// Self-check an extension before registering:\nforeach ($ext->getTokenParsers() as $p) {\n    if (!$p instanceof \\Twig\\TokenParser\\TokenParserInterface) {\n        throw new \\TypeError(get_class($ext).'::getTokenParsers() returned '.get_debug_type($p));\n    }\n}","typeGuard":"function isValidTokenParser(mixed $p): bool {\n    return $p instanceof \\Twig\\TokenParser\\TokenParserInterface;\n}","tryCatchPattern":"try {\n    $twig->addExtension($ext);\n    $twig->loadTemplate('any.twig'); // triggers initExtensions()\n} catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), 'getTokenParsers()')) {\n        // fix or replace the broken extension's getTokenParsers()\n    }\n    throw $e;\n}","preventionTips":["Always return instances (new MyParser()), never class-name strings, from getTokenParsers()","Add a return type (: array) and @return TokenParserInterface[] docblock","Keep getFilters/getTests/getTokenParsers contents matched to their respective getters","Run an environment-initialization smoke test in CI to catch bad extensions early"],"tags":["php","twig","extensions","type-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}