twigphp/Twig · error · LogicException
Unable to add a token parser as extensions have already…
Error message
Unable to add a token parser as extensions have already been initialized.
What it means
Twig's ExtensionSet throws this LogicException when addTokenParser() is called after the extension environment has been initialized. Token parsers must be registered during environment setup, before any template is loaded or rendered, because the parser registry is frozen once extensions are initialized.
Solutions
- Register the token parser (via its extension) immediately after constructing the Environment and before any loadTemplate/render call.
- Wrap the parser in an AbstractExtension::getTokenParsers() and add that extension up front with $twig->addExtension().
- In frameworks (e.g. Symfony), register the extension as a twig.extension tagged service so it is added during container setup.
- If late registration is unavoidable, create a fresh Environment with the parser included instead of mutating the initialized one.
Example fix
// before
$twig = new \Twig\Environment($loader);
$twig->render('home.twig'); // environment now initialized
$twig->getExtension(ExtensionSet::class)->addTokenParser(new MyParser());
// after
$twig = new \Twig\Environment($loader);
$twig->addExtension(new MyExtension()); // getTokenParsers() supplies the parser pre-init
$twig->render('home.twig'); Defensive patterns
Strategy: validation
Validate before calling
// Track initialization yourself and register before first use:
$twig = new \Twig\Environment($loader);
$twig->addExtension(new MyExtension()); // must precede any render/loadTemplate
$twig->render('template.twig'); Try / catch
try {
$twig->getExtension(\Twig\ExtensionSet::class)->addTokenParser($parser);
} catch (\LogicException $e) {
throw new \RuntimeException('Token parser registered too late: register it before the first render', 0, $e);
} Prevention
- Register all extensions/parsers immediately after Environment construction, before any render
- In frameworks, use the extension tagging mechanism (e.g. twig.extension services) instead of imperative late calls
- Never mutate a shared/cached Twig instance at request runtime
- Centralize environment setup in a factory that fully configures it before returning
When it happens
Trigger: Calling addTokenParser() on the ExtensionSet (or via an extension wired to it) after the Twig Environment booted: after any render/loadTemplate/getTemplate call or any access that triggers initExtensions(). Typically registering a custom token parser lazily inside a request handler or render hook.
Common situations: Registering an extension/parser from a framework bundle after container boot and Twig warmup; mutating a shared/cached Twig Environment at request runtime; calling addTokenParser in code that runs after a first render in the same process.
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 test " " as extensions have already been…
- 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/f7da00c5a6ae01c9.
Report an issue: GitHub.
Appendix: source
Thrown at src/ExtensionSet.php:302
$this->staging->addNodeVisitor($visitor);
}
/**
* @return NodeVisitorInterface[]
*/
public function getNodeVisitors(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->visitors;
}
public function addTokenParser(TokenParserInterface $parser): void
{
if ($this->initialized) {
throw new \LogicException('Unable to add a token parser as extensions have already been initialized.');
}
$this->staging->addTokenParser($parser);
}
/**
* @return TokenParserInterface[]
*/
public function getTokenParsers(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->parsers;
}
public function getTokenParser(string $name): ?TokenParserInterfaceView on GitHub (pinned to a414c3a491)