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

  1. Ensure the left operand is an ArrayExpression built from the parsed pattern before constructing the node.
  2. Each pair's 'value' must be an AssignContextVariable — fix the parser so identifiers become AssignContextVariable nodes.
  3. If the LHS is a simple variable assignment, use the plain SetBinary node instead of the destructuring one.
  4. 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

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


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)