twigphp/Twig · error · LogicException
A " " can only contain " " nodes; replacing the macro " "…
Error message
A "%s" can only contain "%s" nodes; replacing the macro "%s" with a "%s" node is not supported.
What it means
MacrosNode::setNode() enforces the same invariant as its constructor: only MacroNode instances may occupy a slot in a MacrosNode. Replacing a macro with any other Node type throws a LogicException, keeping the node tree internally consistent for the compiler.
Solutions
- Only pass \Twig\Node\MacroNode instances to setNode() on a MacrosNode.
- If the replacement is genuinely not a macro, restructure so it lives in a different node container rather than inside MacrosNode.
- Guard with `if ($node instanceof MacroNode)` before calling setNode().
Example fix
// before
$macros->setNode('greet', new \Twig\Node\TextNode('hi'));
// after
if ($node instanceof \Twig\Node\MacroNode) { $macros->setNode('greet', $node); } Defensive patterns
Strategy: type-guard
Validate before calling
if (!$node instanceof \Twig\Node\MacroNode) { throw new LogicException('setNode on MacrosNode requires MacroNode'); } Type guard
function canReplaceMacro(\Twig\Node\Node $node): bool { return $node instanceof \Twig\Node\MacroNode; } Try / catch
try {
$macrosNode->setNode($name, $node);
} catch (\LogicException $e) {
// replacement was not a MacroNode; skip or handle
} Prevention
- Check instanceof MacroNode before any setNode call on MacrosNode
- Keep node visitors limited to transformations that preserve node types
- Write AST-transform tests similar to testItRejectsReplacingAMacroWithANonMacroNode
When it happens
Trigger: Calling `$macrosNode->setNode('name', $someOtherNode)` where the node is not a MacroNode — usually from a custom NodeVisitor or AST-modification test (as in testItRejectsReplacingAMacroWithANonMacroNode).
Common situations: Custom Twig node visitors rewriting macros; tests exercising node immutability rules; automated template transformations that swap nodes without type checks.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Using " " for the macro " " of " " is not supported. You…
- The argument " " in macro " " cannot be defined because the…
- The variadic argument
- Argument " " is defined twice for macro " ".
- Expected endmacro for macro
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/ed53aaf70855b91b.
Report an issue: GitHub.
Appendix: source
Thrown at src/Node/MacrosNode.php:44
{
/**
* @param array<string, MacroNode> $macros
*/
public function __construct(array $macros = [])
{
foreach ($macros as $name => $macro) {
if (!$macro instanceof MacroNode) {
throw new \InvalidArgumentException(\sprintf('Using "%s" for the macro "%s" of "%s" is not supported. You must pass a "%s" instance.', get_debug_type($macro), $name, static::class, MacroNode::class));
}
}
parent::__construct($macros);
}
public function setNode(string $name, Node $node): void
{
if (!$node instanceof MacroNode) {
throw new \LogicException(\sprintf('A "%s" can only contain "%s" nodes; replacing the macro "%s" with a "%s" node is not supported.', static::class, MacroNode::class, $name, get_debug_type($node)));
}
parent::setNode($name, $node);
}
public function compile(Compiler $compiler): void
{
if (!\count($this)) {
return;
}
$compiler
->write("protected function loadDeclaredMacros(): array\n", "{\n")
->indent()
->write("return [\n")
->indent()
;
View on GitHub (pinned to a414c3a491)