twigphp/Twig · error · RuntimeError

Macro " " is not defined in template " ".

Error message

Macro "%s" is not defined in template "%s".

What it means

MacroNamespace::call resolves a macro name within a template's macro namespace; if neither the given name nor (for legacy "macro_"-prefixed names) the bare name resolves to a defined macro, Twig throws this RuntimeError. It means the macro you invoked is not defined (or imported) in the template being executed.

Solutions

  1. Import the template defining the macro first: {% import "forms.twig" as forms %} or {% from "forms.twig" import input %}.
  2. Check spelling/case of the macro name against its {% macro name(...) %} definition.
  3. If using the "macro_"-prefixed name programmatically, pass the bare macro name instead (deprecated since twig/twig 3.29).
  4. Verify the macro exists in the namespace you are calling into (macros are namespaced per importing template).

Example fix

// before (Twig template)
{{ input('text', 'email') }} {# RuntimeError: Macro "input" is not defined #}
// after
{% from "forms.twig" import input %}
{{ input('text', 'email') }}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the macro exists before calling
$src = $twig->load('forms.twig')->getSourceContext()->getCode();
if (!preg_match('/\{%\s*macro\s+input\b/', $src)) {
    throw new RuntimeException('Macro "input" is not defined in forms.twig');
}

Try / catch

try {
    $html = $twig->render('page.twig', $ctx);
} catch (\Twig\Error\RuntimeError $e) {
    if (str_contains($e->getMessage(), 'is not defined in template')) {
        // missing/renamed macro — fix import or name
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling a macro that is not defined in the template: calling without {% import %}/{% from %}, a typo in the macro name, or (twig/twig >= 3.29) programmatically calling via the deprecated "macro_"-prefixed internal name when no such macro exists in the namespace.

Common situations: Refactorings that rename or remove a macro while callers still reference it; forgetting to import the template defining the macro; generated or compiled code using legacy macro_ prefixed names after upgrading twig/twig (deprecation added in 3.29).

Related errors


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

Appendix: source

Thrown at src/MacroNamespace.php:58

        }

        if (!str_starts_with($name, 'macro_') || null === $this->findDeclaredName($bareName = substr($name, \strlen('macro_')), $context)) {
            return false;
        }

        trigger_deprecation('twig/twig', '3.29', 'Testing whether the macro "%s" is defined via the "macro_"-prefixed name "%s" is deprecated; pass the bare macro name to "%s" instead.', $bareName, $name, MacroReferenceExpression::class);

        return true;
    }

    /**
     * @param array<int|string, mixed> $arguments
     */
    public function call(string $name, array $arguments, array $context, int $line, Source $source): string|Markup
    {
        if (null === $macro = $this->resolve($name, $context)) {
            if (!str_starts_with($name, 'macro_') || null === $macro = $this->resolve($bareName = substr($name, \strlen('macro_')), $context)) {
                throw new RuntimeError(\sprintf('Macro "%s" is not defined in template "%s".', $name, $this->template->getTemplateName()), $line, $source);
            }

            trigger_deprecation('twig/twig', '3.29', 'Calling the macro "%s" via the "macro_"-prefixed name "%s" is deprecated; pass the bare macro name to "%s" instead.', $bareName, $name, MacroReferenceExpression::class);
        }

        return $macro->callLegacy($arguments, $source, $line);
    }

    /**
     * @return array{string, string}|null
     */
    private function findDeclaredName(string $name, array $context): ?array
    {
        $namespace = $this;
        while (true) {
            if (isset($namespace->macros[$name])) {
                return [$name, $namespace->template->getTemplateName()];
            }

View on GitHub (pinned to a414c3a491)