{"record":{"id":"8e1128f5fe94272d","repo":"twigphp/Twig","slug":"unable-to-add-filter-s-as-extensions-have-already-been","errorCode":null,"errorMessage":"Unable to add filter \"%s\" as extensions have already been initialized.","messagePattern":"Unable to add filter \"(.+?)\" as extensions have already been initialized\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/ExtensionSet.php","lineNumber":225,"sourceCode":"                return $function;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * @param callable(string): (TwigFunction|false) $callable\n     */\n    public function registerUndefinedFunctionCallback(callable $callable): void\n    {\n        $this->functionCallbacks[] = $callable;\n    }\n\n    public function addFilter(TwigFilter $filter): void\n    {\n        if ($this->initialized) {\n            throw new \\LogicException(\\sprintf('Unable to add filter \"%s\" as extensions have already been initialized.', $filter->getName()));\n        }\n\n        $this->staging->addFilter($filter);\n    }\n\n    /**\n     * @return TwigFilter[]\n     */\n    public function getFilters(): array\n    {\n        if (!$this->initialized) {\n            $this->initExtensions();\n        }\n\n        return $this->filters;\n    }\n\n    public function getFilter(string $name): ?TwigFilter","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/ExtensionSet.php#L207-L243","documentation":"TwigFilter registration goes through ExtensionSet::addFilter, which is only valid before extension initialization. Once initialized, the filter table is built and frozen, so late addFilter calls throw this LogicException.","triggerScenarios":"Calling $twig->addFilter(...) after any render/loadTemplate/getFilter call initialized the extension set; adding filters conditionally after an earlier template was rendered in the same request.","commonSituations":"Registering filters in a middleware/controller after twig rendered an error page; scripts that render a first template early then set up custom filters; shared Environment objects in test suites mutated mid-suite.","solutions":["Register all filters immediately after Environment construction.","Move filters into an Extension class added at construction time.","Instantiate a fresh Environment when a different filter set is needed.","In frameworks, expose the filter via an extension service instead of direct addFilter calls."],"exampleFix":"// before\n$twig = new Environment($loader);\n$twig->render('x.twig');\n$twig->addFilter(new TwigFilter('shout', fn($s) => strtoupper($s)));\n\n// after\n$twig = new Environment($loader);\n$twig->addFilter(new TwigFilter('shout', fn($s) => strtoupper($s)));\n$twig->render('x.twig');","handlingStrategy":"try-catch","validationCode":"// register before first use\nif (!isset($GLOBALS['twig_initialized'])) {\n    $twig->addFilter($filter);\n}","typeGuard":null,"tryCatchPattern":"try { $twig->addFilter($filter); } catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), 'already been initialized')) {\n        // defer to a new Environment or move registration to bootstrap\n    } else { throw $e; }\n}","preventionTips":["Register all filters during bootstrap, before any render/compile call.","Package filters in an Extension class added at construction time.","Avoid conditionally adding filters after partial page renders.","Audit request lifecycle hooks for late twig mutations."],"tags":["twig","extension","filter","initialization-order"],"backgroundTag":"invalid-state-transition","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"}