twigphp/Twig · error · SyntaxError
Multiple extends tags are forbidden.
Error message
Multiple extends tags are forbidden.
What it means
A template may extend only one parent, so a second {% extends %} tag makes inheritance ambiguous. checkExtendsTag throws this SyntaxError when hasExtends is already true (a previous extends tag was seen in the same template).
Solutions
- Keep exactly one {% extends %} tag per template
- Use conditional inheritance with a single tag: {% extends condition ? 'a.html.twig' : 'b.html.twig' %}
- Remove the leftover duplicate extends line from a merge/refactor
Example fix
// before
{% extends 'a.html.twig' %}
{% extends 'b.html.twig' %}
// after
{% extends is_mobile ? 'mobile.html.twig' : 'base.html.twig' %} Defensive patterns
Strategy: validation
Validate before calling
$n = preg_match_all('/{%-?\\s*extends\\b/', $tpl); if ($n > 1) { throw new \InvalidArgumentException('multiple extends tags'); } Try / catch
try { $env->render($name); } catch (\Twig\Error\SyntaxError $e) { if (str_contains($e->getMessage(), 'Multiple extends')) { /* fix template */ } } Prevention
- One extends per template, using a ternary expression for dynamic parents
- Grep templates in CI for multiple extends tags
- Avoid merges that concatenate template files
When it happens
Trigger: Two or more {% extends %} tags in one template file; also triggered via Parser::setParent when setParent is called a second time with throwOnMultiple=true.
Common situations: Concatenating or merging templates so two extends lines remain; conditional inheritance attempts where developers expect only one branch to count — Twig parses all tags statically; copy-paste duplicates left after refactoring.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Calling the "parent" function outside of a block is…
- Calling the "parent" function on a template that does not…
- A template that extends another one cannot include content…
- Cannot use "extend" in a block.
- Cannot use "extend" in a macro.
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/e5607cedc9b1871f.
Report an issue: GitHub.
Appendix: source
Thrown at src/NodeVisitor/CorrectnessNodeVisitor.php:196
$this->checkExtendsTag($node);
}
if (!isset($this->rootNodes[$node])) {
trigger_deprecation('twig/twig', '3.28', 'Using the "%s" tag outside the root of a template is deprecated in %s at line %d.', $node->getNodeTag(), $node->getSourceContext()->getName(), $node->getTemplateLine());
}
}
private function checkExtendsTag(ConfigNode $node): void
{
// "extends" inside a "block" or a "macro" has always been a hard error; keep it
if ($this->blockDepth) {
throw new SyntaxError('Cannot use "extend" in a block.', $node->getTemplateLine(), $node->getSourceContext());
}
if ($this->macroDepth) {
throw new SyntaxError('Cannot use "extend" in a macro.', $node->getTemplateLine(), $node->getSourceContext());
}
if ($this->hasExtends) {
throw new SyntaxError('Multiple extends tags are forbidden.', $node->getTemplateLine(), $node->getSourceContext());
}
$this->hasExtends = true;
}
private function checkBlockDefinitionNesting(BlockReferenceNode $node): void
{
// A "block" definition nested under an output-wrapping tag is registered globally
// regardless of that tag, so the nesting is misleading. This only matters at the root
// of a child template's body: once inside a block, a macro, an output capture, or in
// a standalone template, the block is rendered in place and behaves like any other.
if (!$this->hasParent || $this->blockDepth || $this->macroDepth || $this->capturingNodeDepth || !$this->tagStack) {
return;
}
$tag = $this->tagStack[array_key_last($this->tagStack)];
throw new SyntaxError(\sprintf('A "block" tag cannot be under a "%s" tag (line %d).', $tag->getNodeTag(), $tag->getTemplateLine()), $node->getTemplateLine(), $node->getSourceContext());
}View on GitHub (pinned to a414c3a491)