twigphp/Twig · error · SyntaxError

Cannot use "extend" in a block.

Error message

Cannot use "extend" in a block.

What it means

Twig forbids placing an {% extends %} tag inside a {% block %}. A block is rendered within the child/parent rendering flow, and inheritance must be decided before any block is rendered, so checkExtendsTag throws a SyntaxError when blockDepth is non-zero.

Solutions

  1. Move {% extends %} to the very top of the template, outside any block
  2. Remove the extends if the template should be standalone
  3. Split into two templates: one that extends, one that only defines the block content

Example fix

// before
{% block content %}
  {% extends 'base.html.twig' %}
{% endblock %}
// after
{% extends 'base.html.twig' %}
{% block content %}...{% endblock %}
Defensive patterns

Strategy: validation

Validate before calling

if (preg_match('/{%-?\\s*extends\\b/', $blockBody)) { throw new \InvalidArgumentException('extends not allowed inside block'); }

Try / catch

try { $env->render($name); } catch (\Twig\Error\SyntaxError $e) { /* 'extend' placement error */ }

Prevention

When it happens

Trigger: An {% extends %} tag nested inside a {% block %} ... {% endblock %} body; detected in checkExtendsTag (called from checkConfigTag) when $this->blockDepth > 0.

Common situations: Accidentally indenting {% extends %} inside a block during refactoring; generating templates programmatically and wrapping everything in a block; copy-pasting a whole template body (including its extends) into a block.

Related errors


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

Appendix: source

Thrown at src/NodeVisitor/CorrectnessNodeVisitor.php:190

        }
    }

    private function checkConfigTag(ConfigNode $node): void
    {
        if ('extends' === $node->getNodeTag()) {
            $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) {

View on GitHub (pinned to a414c3a491)