twigphp/Twig · error · LogicException

Unable to add a node visitor as extensions have already…

Error message

Unable to add a node visitor as extensions have already been initialized.

What it means

Node visitors (NodeVisitorInterface) are collected during extension initialization and applied at compile time. ExtensionSet::addNodeVisitor rejects late additions with this LogicException because the compilation pipeline may already have run (or be cached), making the visitor addition ineffective — so Twig fails loudly instead.

Solutions

  1. Add node visitors at bootstrap, right after creating the Environment, before any compile/render.
  2. Attach the visitor via a custom Extension class registered at construction.
  3. Use a fresh Environment when visitors must differ per run.
  4. Verify with debug tooling (e.g. Twig profiler) whether an earlier render already initialized extensions.

Example fix

// before
$twig = new Environment($loader);
$twig->render('a.twig');
$twig->addNodeVisitor(new MyVisitor()); // too late

// after
$twig = new Environment($loader);
$twig->addNodeVisitor(new MyVisitor());
$twig->render('a.twig');
Defensive patterns

Strategy: try-catch

Validate before calling

// node visitors must be added before first compile; do it at construction time
$twig = new \Twig\Environment($loader);
$twig->addNodeVisitor($visitor); // before any render/loadTemplate

Try / catch

try { $twig->addNodeVisitor($visitor); } catch (\LogicException $e) {
    if (str_contains($e->getMessage(), 'already been initialized')) {
        $twig = new \Twig\Environment($loader);
        $twig->addNodeVisitor($visitor);
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Calling $twig->addNodeVisitor(...) after the extension set was initialized by a previous render/compile/loadTemplate call; adding visitors from a runtime hook that executes post-compilation.

Common situations: Profiling/optimization visitors added after a first render warmed caches; debug visitors registered conditionally mid-request; test bootstrap adding visitors per-test to a cached Environment.

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


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/f204b5f2877e2082. Report an issue: GitHub.

Appendix: source

Thrown at src/ExtensionSet.php:281

                return $filter;
            }
        }

        return null;
    }

    /**
     * @param callable(string): (TwigFilter|false) $callable
     */
    public function registerUndefinedFilterCallback(callable $callable): void
    {
        $this->filterCallbacks[] = $callable;
    }

    public function addNodeVisitor(NodeVisitorInterface $visitor): void
    {
        if ($this->initialized) {
            throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.');
        }

        $this->staging->addNodeVisitor($visitor);
    }

    /**
     * @return NodeVisitorInterface[]
     */
    public function getNodeVisitors(): array
    {
        if (!$this->initialized) {
            $this->initExtensions();
        }

        return $this->visitors;
    }

    public function addTokenParser(TokenParserInterface $parser): void

View on GitHub (pinned to a414c3a491)