twigphp/Twig · error · SyntaxError

Calling the "parent" function outside of a block is…

Error message

Calling the "parent" function outside of a block is forbidden.

What it means

CoreExtension::parseParentFunction handles the `parent()` call during template compilation. The `parent()` function is only meaningful inside a `{% block %}`; Twig's parser enforces this by peeking at the block stack and throwing a SyntaxError (compile time, not runtime) when the call appears outside any block. Throwing at parse time gives a precise template line and source context.

Solutions

  1. Move the `{{ parent() }}` call inside a `{% block name %}...{% endblock %}` definition.
  2. If no parent rendering is intended, remove the parent() call entirely.
  3. When reusing block-like markup, use `{% block %}` overrides in a template that extends another, rather than parent() in shared partials.
  4. Re-read the SyntaxError's line/source context to find the misplaced call and relocate it.

Example fix

// before (Twig)
{{ parent() }}
{% block content %}...{% endblock %}

// after
{% block content %}
  {{ parent() }}
  ...
{% endblock %}
Defensive patterns

Strategy: validation

Validate before calling

// CI lint step (Symfony)
// php bin/console lint:twig templates/ --format=github
// fails the build on 'Calling the "parent" function outside of a block' before deploy

Try / catch

try {
    $twig->parse($twig->tokenize(new \Twig\Source($templateCode, $name)));
} catch (\Twig\Error\SyntaxError $e) {
    if (str_contains($e->getMessage(), 'outside of a block is forbidden')) {
        // report template + line for the developer to fix
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Writing `{{ parent() }}` (or `{{ parent() }}`-style calls) at template top level, inside a macro, inside a `{% set %}` or `{% filter %}` outside of any block definition — anywhere `$parser->peekBlockStack()` returns null. Note `{% block %}` must still be open: parent() must be lexically inside the block body.

Common situations: Copy-pasting `{{ parent() }}` from a child block into a top-level include or macro; moving code out of a block during a refactor and forgetting the parent() call; using parent() inside an embedded template's include rather than its own block override.

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/41f99bfa650411b4. Report an issue: GitHub.

Appendix: source

Thrown at src/Extension/CoreExtension.php:2224

            }
        } catch (\Throwable $e) {
            while (ob_get_level() > $level) {
                ob_end_clean();
            }

            throw $e;
        }

        return ob_get_clean();
    }

    /**
     * @internal
     */
    public static function parseParentFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression
    {
        if (!$blockName = $parser->peekBlockStack()) {
            throw new SyntaxError('Calling the "parent" function outside of a block is forbidden.', $line, $parser->getStream()->getSourceContext());
        }

        if (!$parser->hasInheritance()) {
            throw new SyntaxError('Calling the "parent" function on a template that does not call "extends" or "use" is forbidden.', $line, $parser->getStream()->getSourceContext());
        }

        return new ParentExpression($blockName, $line);
    }

    /**
     * @internal
     */
    public static function parseBlockFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression
    {
        $fakeFunction = new TwigFunction('block', static fn ($name, $template = null) => null);
        $args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($args);

        return new BlockReferenceExpression($args[0], $args[1] ?? null, $line);

View on GitHub (pinned to a414c3a491)