twigphp/Twig · error · LogicException
Left side must be ArrayExpression for object/mapping…
Error message
Left side must be ArrayExpression for object/mapping destructuring.
What it means
This node compiles object/mapping destructuring assignments (e.g. {a, b} = expr in Twig expression syntax). Its constructor contract requires the left side to be an ArrayExpression containing the destructuring pattern; anything else (a NameExpression, a literal, etc.) is a parser-level contract violation, so a LogicException is thrown at node construction time.
Solutions
- Ensure the left operand is an ArrayExpression built from the parsed pattern before constructing the node.
- Each pair's 'value' must be an AssignContextVariable — fix the parser so identifiers become AssignContextVariable nodes.
- If the LHS is a simple variable assignment, use the plain SetBinary node instead of the destructuring one.
- Catch the LogicException at parser/extension construction to surface which syntax produced the malformed LHS.
Example fix
// before $node = new ObjectDestructuringSetBinary($nameExpr, $rhs, $line); // $nameExpr is a NameExpression // after $pattern = new ArrayExpression($pairs, $line); // pairs whose values are AssignContextVariable $node = new ObjectDestructuringSetBinary($pattern, $rhs, $line);
Defensive patterns
Strategy: type-guard
Validate before calling
if (!$left instanceof \Twig\Node\Expression\ArrayExpression) {
throw new \SyntaxError('Object/mapping destructuring requires an array-expression pattern on the left.', $lineno);
} Type guard
function isDestructuringPattern(\Twig\Node\Node $n): bool { return $n instanceof \Twig\Node\Expression\ArrayExpression; } Try / catch
try {
$node = new ObjectDestructuringSetBinary($left, $right, $lineno);
} catch (\LogicException $e) {
// LHS wasn't an ArrayExpression — fall back to plain SetBinary or raise a SyntaxError
} Prevention
- Always parse the destructuring pattern into ArrayExpression first.
- Ensure every pair value is an AssignContextVariable before building the node.
- Add parser unit tests covering both destructuring and plain assignment LHS.
When it happens
Trigger: Constructing ObjectDestructuringSetBinary directly with a non-ArrayExpression first argument; a custom expression parser producing a destructuring Set node without first parsing the left-hand side into an ArrayExpression of key/value pairs; misuse when adapting the parser to new syntax.
Common situations: Custom Twig operators/parsers (e.g. via an extension adding destructuring-like syntax) passing the wrong node type; template syntax parsed by a modified grammar where the LHS wasn't wrapped in an array expression; library version drift where the node class API changed.
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
- The "name" attribute must be a string.
- EmptyNode cannot have children.
- A documentation target can only be set while parsing a tag.
- The documentation target for a tag can only be set once.
- The "format_list" filter requires the "IntlListFormatter"…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/50658a283132d239.
Report an issue: GitHub.
Appendix: source
Thrown at src/Node/Expression/Binary/ObjectDestructuringSetBinary.php:37
use Twig\Node\Expression\Variable\AssignContextVariable;
use Twig\Node\Node;
/**
* @internal
*/
class ObjectDestructuringSetBinary extends AbstractBinary
{
/** @var list<array{property: string, variable: string}> */
private array $mappings = [];
/**
* @param ArrayExpression $left The array expression containing object/mapping destructuring properties
* @param AbstractExpression $right The expression providing values for assignment
*/
public function __construct(Node $left, Node $right, int $lineno)
{
if (!$left instanceof ArrayExpression) {
throw new \LogicException('Left side must be ArrayExpression for object/mapping destructuring.');
}
foreach ($left->getKeyValuePairs() as $pair) {
if (!$pair['value'] instanceof AssignContextVariable) {
throw new SyntaxError(\sprintf('Cannot assign to "%s", only variables can be assigned in object/mapping destructuring.', $pair['value']::class), $lineno);
}
$this->mappings[] = [
'property' => $pair['key']->getAttribute('value'),
'variable' => $pair['value']->getAttribute('name'),
];
}
parent::__construct($left, $right, $lineno);
}
public function compile(Compiler $compiler): void
{
$compiler->addDebugInfo($this);View on GitHub (pinned to a414c3a491)