twigphp/Twig · error · LogicException
Unable to register extension
Error message
Unable to register extension "%s" as extensions have already been initialized.
What it means
ExtensionSet only accepts new extensions while the environment is uninitialized. Once extensions have been initialized (lazily, usually on first render/load), the set of functions, filters, tags and tests is frozen; calling addExtension afterward throws this LogicException because the compiled/cached state already reflects the old extension set.
Solutions
- Register all extensions immediately after creating the Environment, before any render/load call.
- Ensure testMultipleRegistrations-style setup runs once at bootstrap, not per render.
- Create a fresh Environment instance if you genuinely need a different extension set at runtime.
- If lazy, force extension registration into the service factory that builds the Twig environment.
Example fix
// before
$twig = new \Twig\Environment($loader);
echo $twig->render('hi.twig');
$twig->addExtension(new MyExtension()); // too late
// after
$twig = new \Twig\Environment($loader);
$twig->addExtension(new MyExtension());
echo $twig->render('hi.twig'); Defensive patterns
Strategy: try-catch
Validate before calling
// register early: do this only if extensions are not yet initialized
if (!$env->hasExtension('Twig\Extension\CoreExtension') || !method_exists($env, 'render') || true) {
// best guard: add extensions immediately after construction, before first render
} Try / catch
try { $twig->addExtension($ext); } catch (\LogicException $e) {
if (str_contains($e->getMessage(), 'already been initialized')) {
$twig = new \Twig\Environment($loader, $options);
$twig->addExtension($ext); // rebuild environment
} else { throw $e; }
} Prevention
- Register every extension immediately after constructing the Environment.
- Never render/load templates before extension setup completes.
- In frameworks, use the official extension-registration mechanism (bundles/services).
- In tests, create a fresh Environment per test instead of mutating shared ones.
When it happens
Trigger: Calling $twig->addExtension(...) after Twig has already initialized extensions — typically after the first render(), loadTemplate(), or token parse in the same request; also via setExtensions() late in the lifecycle.
Common situations: Registering extensions inside a controller/event handler that runs after some earlier code already rendered a template; Symfony apps calling addExtension after the Twig service was first used; test suites reusing an Environment across tests and adding extensions per-test.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Unable to add function
- Unable to add filter
- Unable to add a node visitor as extensions have already…
- The " " extension is not enabled.
- Tag " " is already registered.
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/250e7dcaf2f9b76d.
Report an issue: GitHub.
Appendix: source
Thrown at src/ExtensionSet.php:156
if (is_file($r->getFileName())) {
$lastModified = max(filemtime($r->getFileName()), $lastModified);
}
}
}
return $this->lastModified = $lastModified;
}
public function addExtension(ExtensionInterface $extension): void
{
if ($extension instanceof AttributeExtension) {
$class = $extension->getClass();
} else {
$class = $extension::class;
}
if ($this->initialized) {
throw new \LogicException(\sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class));
}
if (isset($this->extensions[$class])) {
throw new \LogicException(\sprintf('Unable to register extension "%s" as it is already registered.', $class));
}
$this->extensions[$class] = $extension;
}
public function addFunction(TwigFunction $function): void
{
if ($this->initialized) {
throw new \LogicException(\sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName()));
}
$this->staging->addFunction($function);
}
View on GitHub (pinned to a414c3a491)