{"record":{"id":"5a92b2627e279104","repo":"twigphp/Twig","slug":"unable-to-add-function-s-as-extensions-have-already-been","errorCode":null,"errorMessage":"Unable to add function \"%s\" as extensions have already been initialized.","messagePattern":"Unable to add function \"(.+?)\" as extensions have already been initialized\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/ExtensionSet.php","lineNumber":169,"sourceCode":"        } else {\n            $class = $extension::class;\n        }\n\n        if ($this->initialized) {\n            throw new \\LogicException(\\sprintf('Unable to register extension \"%s\" as extensions have already been initialized.', $class));\n        }\n\n        if (isset($this->extensions[$class])) {\n            throw new \\LogicException(\\sprintf('Unable to register extension \"%s\" as it is already registered.', $class));\n        }\n\n        $this->extensions[$class] = $extension;\n    }\n\n    public function addFunction(TwigFunction $function): void\n    {\n        if ($this->initialized) {\n            throw new \\LogicException(\\sprintf('Unable to add function \"%s\" as extensions have already been initialized.', $function->getName()));\n        }\n\n        $this->staging->addFunction($function);\n    }\n\n    /**\n     * @return TwigFunction[]\n     */\n    public function getFunctions(): array\n    {\n        if (!$this->initialized) {\n            $this->initExtensions();\n        }\n\n        return $this->functions;\n    }\n\n    public function getFunction(string $name): ?TwigFunction","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/ExtensionSet.php#L151-L187","documentation":"Like addExtension, adding a Twig function is only allowed before extensions are initialized. After initialization the function table is built and frozen (and may be cached), so ExtensionSet::addFunction throws this LogicException for late additions.","triggerScenarios":"Calling $twig->addFunction(...) after the environment has been used (render/loadTemplate/getTokenParsers) which triggers lazy initialization; also calling it on an ExtensionSet already flagged initialized.","commonSituations":"Defining twig functions in a listener that fires post-boot; adding functions conditionally inside a loop that runs after first render; tests adding functions per-test on a shared Environment.","solutions":["Call addFunction right after constructing the Environment, before rendering anything.","Alternatively wrap the function in a proper Extension class registered at construction time.","Create a new Environment if late registration is truly required.","In Symfony, register the function via a TwigExtension service so the framework adds it at boot."],"exampleFix":"// before\n$twig = new Environment($loader);\n$twig->render('page.twig');\n$twig->addFunction(new TwigFunction('price', fn($v) => $v * 1.2));\n\n// after\n$twig = new Environment($loader);\n$twig->addFunction(new TwigFunction('price', fn($v) => $v * 1.2));\n$twig->render('page.twig');","handlingStrategy":"try-catch","validationCode":"// guard: register before initialization\n$functionsAdded = false;\nfunction addFunctionEarly(\\Twig\\Environment $twig, \\Twig\\TwigFunction $fn): void {\n    global $functionsAdded;\n    if ($functionsAdded) { throw new \\LogicException('Too late to add functions'); }\n}","typeGuard":null,"tryCatchPattern":"try { $twig->addFunction($fn); } catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), 'already been initialized')) {\n        // move to bootstrap or recreate the environment\n    } else { throw $e; }\n}","preventionTips":["Add all functions right after Environment construction.","Wrap functions in an Extension class so the framework registers them at boot.","Do not render any template before finishing Twig setup.","In tests, build a fresh Environment whenever functions change."],"tags":["twig","extension","function","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"}