twigphp/Twig · error · LogicException

Unable to add function

Error message

Unable to add function "%s" as extensions have already been initialized.

What it means

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.

Solutions

  1. Call addFunction right after constructing the Environment, before rendering anything.
  2. Alternatively wrap the function in a proper Extension class registered at construction time.
  3. Create a new Environment if late registration is truly required.
  4. In Symfony, register the function via a TwigExtension service so the framework adds it at boot.

Example fix

// before
$twig = new Environment($loader);
$twig->render('page.twig');
$twig->addFunction(new TwigFunction('price', fn($v) => $v * 1.2));

// after
$twig = new Environment($loader);
$twig->addFunction(new TwigFunction('price', fn($v) => $v * 1.2));
$twig->render('page.twig');
Defensive patterns

Strategy: try-catch

Validate before calling

// guard: register before initialization
$functionsAdded = false;
function addFunctionEarly(\Twig\Environment $twig, \Twig\TwigFunction $fn): void {
    global $functionsAdded;
    if ($functionsAdded) { throw new \LogicException('Too late to add functions'); }
}

Try / catch

try { $twig->addFunction($fn); } catch (\LogicException $e) {
    if (str_contains($e->getMessage(), 'already been initialized')) {
        // move to bootstrap or recreate the environment
    } else { throw $e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/5a92b2627e279104. Report an issue: GitHub.

Appendix: source

Thrown at src/ExtensionSet.php:169

        } 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);
    }

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

        return $this->functions;
    }

    public function getFunction(string $name): ?TwigFunction

View on GitHub (pinned to a414c3a491)