twigphp/Twig · error · LogicException
EmptyNode cannot have children.
Error message
EmptyNode cannot have children.
What it means
EmptyNode is Twig's placeholder node representing 'nothing' (e.g. an omitted block of an if/for). As an intentional leaf, its setNode() is overridden to always throw: the compiler must never attach children to an EmptyNode. If you reach this, some code is trying to fill in a node slot that is supposed to stay empty.
Solutions
- Before calling setNode(), check the target is a real node: if (!$node instanceof EmptyNode) { $node->setNode(...); } and skip or replace the placeholder otherwise.
- Replace the EmptyNode with the concrete node type first (e.g. construct the intended Node and swap it via setNode on the PARENT, not on the EmptyNode).
- In a NodeVisitor, implement the swap in leaveNode() by returning a new node instead of mutating the EmptyNode.
- If this comes from a third-party extension, update the extension; EmptyNode's immutable no-children contract is by design.
Example fix
// before
$node->setNode('body', $newBody); // crashes when $node is EmptyNode
// after
if (!$node instanceof \Twig\Node\EmptyNode) {
$node->setNode('body', $newBody);
} Defensive patterns
Strategy: type-guard
Validate before calling
if ($node instanceof \Twig\Node\EmptyNode) {
throw new \LogicException('Cannot attach children to an EmptyNode placeholder; replace the placeholder in its parent instead.');
} Type guard
function isAttachableNode(\Twig\Node\Node $n): bool { return !$n instanceof \Twig\Node\EmptyNode; } Try / catch
try {
$node->setNode($name, $child);
} catch (\LogicException $e) {
// swap the EmptyNode out at its parent instead of mutating it
} Prevention
- Treat EmptyNode as immutable placeholder — never a container.
- In NodeVisitors, replace nodes by returning a new node from leaveNode() rather than mutating in place.
- Instanceof-check before every setNode call on nodes obtained from parsing.
When it happens
Trigger: Calling ->setNode('...', $node) directly on an EmptyNode instance, or a NodeVisitor/compiler pass written by you or an extension that unconditionally assigns children to the node returned from a parse slot without checking it's an EmptyNode placeholder.
Common situations: Custom NodeVisitor mutating nodes without instanceof checks; custom expression parsers reusing EmptyNode as a generic container; a visitor that rewrites nodes in leaveNode() but hits the empty placeholder branch of a construct.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cannot pop state without a previous state.
- Left side must be ArrayExpression for object/mapping…
- The "name" attribute must be a string.
- Token of type " " does not exist.
- The "format_list" filter requires the "IntlListFormatter"…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/e120a3ad10691d0c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Node/EmptyNode.php:31
use Twig\Attribute\YieldReady;
/**
* Represents an empty node.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
#[YieldReady]
final class EmptyNode extends Node
{
public function __construct(int $lineno = 0)
{
parent::__construct([], [], $lineno);
}
public function setNode(string $name, Node $node): void
{
throw new \LogicException('EmptyNode cannot have children.');
}
}
View on GitHub (pinned to a414c3a491)