twigphp/Twig · error · LogicException
Unable to add test " " as extensions have already been…
Error message
Unable to add test "%s" as extensions have already been initialized.
What it means
ExtensionSet throws this LogicException when addTest() is invoked after extensions have been initialized. Custom TwigTest instances (expression tests like `is odd`) must be registered during setup; the test registry is frozen once the environment initializes so template compilation sees a stable set.
Solutions
- Declare the TwigTest in AbstractExtension::getTests() and register that extension with addExtension() before any render/loadTemplate call.
- Call addTest() only during environment construction, never after the first render or template load.
- In Symfony or similar, expose the test via a twig.extension tagged service so registration happens during environment boot.
- If late registration is required, rebuild a new Environment with the test included rather than mutating the initialized one.
Example fix
// before
$twig = new \Twig\Environment($loader);
$twig->render('x.twig');
$twig->getExtension(ExtensionSet::class)->addTest(new \Twig\TwigTest('even2', fn ($v) => $v % 2 === 0));
// after
class MyExtension extends \Twig\Extension\AbstractExtension {
public function getTests(): array {
return [new \Twig\TwigTest('even2', fn ($v) => $v % 2 === 0)];
}
}
$twig = new \Twig\Environment($loader);
$twig->addExtension(new MyExtension());
$twig->render('x.twig'); Defensive patterns
Strategy: validation
Validate before calling
// Ensure the extension with the test is registered before first use:
$twig = new \Twig\Environment($loader);
$twig->addExtension(new MyExtension()); // getTests() supplied here
$twig->render('template.twig'); // only afterwards Try / catch
try {
$twig->getExtension(\Twig\ExtensionSet::class)->addTest($test);
} catch (\LogicException $e) {
throw new \RuntimeException('Twig test must be registered before environment initialization', 0, $e);
} Prevention
- Declare custom tests in AbstractExtension::getTests() and register the extension up front
- Register all extensions before the first loadTemplate/getTemplate/render call
- Avoid storing long-lived environments and mutating them later; rebuild instead
- Use a bootstrap/factory function that returns a fully configured environment
When it happens
Trigger: Calling addTest() on the ExtensionSet after initExtensions() ran — i.e., after any render/loadTemplate/getTemplate call or after earlier access to filters/tests/functions/globals. Common with lazy runtime registration of custom tests.
Common situations: Adding a custom test lazily at runtime; registering tests from a service after the Twig environment warmed; mutating a shared/cached environment after the first render; framework apps where the environment boots before the extension that adds the test runs.
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 a token parser as extensions have already…
- getTokenParsers() must return an array of…
- " ::getOperators()" must return an array with operators…
- " ::getOperators()" must return an array of 2 elements, got…
- The "format_list" filter requires the "IntlListFormatter"…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/3523b402b1f6a12f.
Report an issue: GitHub.
Appendix: source
Thrown at src/ExtensionSet.php:380
$globals = array_merge($globals, $extension->getGlobals());
}
if ($this->initialized) {
$this->globals = $globals;
}
return $globals;
}
public function resetGlobals(): void
{
$this->globals = null;
}
public function addTest(TwigTest $test): void
{
if ($this->initialized) {
throw new \LogicException(\sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName()));
}
$this->staging->addTest($test);
}
/**
* @return TwigTest[]
*/
public function getTests(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->tests;
}
public function getTest(string $name): ?TwigTestView on GitHub (pinned to a414c3a491)