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
- Import the template defining the macro first: {% import "forms.twig" as forms %} or {% from "forms.twig" import input %}.
- Check spelling/case of the macro name against its {% macro name(...) %} definition.
- If using the "macro_"-prefixed name programmatically, pass the bare macro name instead (deprecated since twig/twig 3.29).
- 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
- Always import macro templates ({% import %}/{% from %}) before calling macros.
- Keep macro names centralized to avoid rename drift.
- After upgrading twig/twig 3.29+, replace macro_-prefixed calls with bare macro names.
- Add compile/render smoke tests for templates using macros.
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
- Argument " " is defined twice for macro " ".
- Positional arguments cannot be used after named arguments…
- The variadic argument
- The variadic argument
- Variable does not exist.
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)