{"record":{"id":"176d46f1c1ef75f0","repo":"twigphp/Twig","slug":"invalid-associativity-s-for-operator-s","errorCode":null,"errorMessage":"Invalid associativity \"%s\" for operator \"%s\".","messagePattern":"Invalid associativity \"(.+?)\" for operator \"(.+?)\"\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/ExtensionSet.php","lineNumber":524,"sourceCode":"\n        $operators = $extension->getOperators();\n        if (!\\is_array($operators)) {\n            throw new \\InvalidArgumentException(\\sprintf('\"%s::getOperators()\" must return an array with operators, got \"%s\".', $extension::class, get_debug_type($operators).(\\is_resource($operators) ? '' : '#'.$operators)));\n        }\n\n        if (2 !== \\count($operators)) {\n            throw new \\InvalidArgumentException(\\sprintf('\"%s::getOperators()\" must return an array of 2 elements, got %d.', $extension::class, \\count($operators)));\n        }\n\n        $expressionParsers = [];\n        foreach ($operators[0] as $operator => $op) {\n            $expressionParsers[] = new UnaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []);\n        }\n        foreach ($operators[1] as $operator => $op) {\n            $op['associativity'] = match ($op['associativity']) {\n                1 => InfixAssociativity::Left,\n                2 => InfixAssociativity::Right,\n                default => throw new \\InvalidArgumentException(\\sprintf('Invalid associativity \"%s\" for operator \"%s\".', $op['associativity'], $operator)),\n            };\n\n            if (isset($op['callable'])) {\n                $expressionParsers[] = $this->convertInfixExpressionParser($op['class'], $operator, $op['precedence'], $op['associativity'], $op['precedence_change'] ?? null, $op['aliases'] ?? [], $op['callable']);\n            } else {\n                $expressionParsers[] = new BinaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['associativity'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []);\n            }\n        }\n\n        if (\\count($expressionParsers)) {\n            trigger_deprecation('twig/twig', '3.21', \\sprintf('Extension \"%s\" uses the old signature for \"getOperators()\", please implement \"getExpressionParsers()\" instead.', $extension::class));\n\n            $this->expressionParsers->add($expressionParsers);\n        }\n    }\n\n    private function convertInfixExpressionParser(string $nodeClass, string $operator, int $precedence, InfixAssociativity $associativity, ?PrecedenceChange $precedenceChange, array $aliases, callable $callable): InfixExpressionParserInterface\n    {","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/ExtensionSet.php#L506-L542","documentation":"Twig's ExtensionSet::initExtension converts legacy integer associativity codes from operator definitions into InfixAssociativity enums. Only 1 (left) and 2 (right) are valid; any other integer (typically 0, -1, or 3) means the extension registered a binary operator with a malformed associativity value, so the library refuses to build the parser.","triggerScenarios":"An extension (or custom AbstractExtension::getOperators()) returns an infix operator definition whose 'associativity' key is not 1 or 2 — e.g. the old convention 0 for 'none', a typo, or a third-party Twig extension written for an older Twig version whose codes changed.","commonSituations":"Upgrading Twig and using a third-party extension still returning legacy integer codes; hand-writing getOperators() and guessing that 0 means non-associative (it does not — there is no 'none' option here); copying an operator array and editing the precedence but corrupting associativity.","solutions":["Check the extension's getOperators() infix (second) array: every operator's 'associativity' must be the integer 1 (left) or 2 (right).","Identify the offending operator from the message (%2$s in the sprintf) and fix or remove its associativity entry.","Update or replace the third-party Twig extension so it returns InfixAssociativity enums or the new-style definition compatible with your Twig version.","If you intended a non-associative operator, remove the associativity expectation and check your Twig version's parser API — use the current InfixAssociativity enum, not legacy integers."],"exampleFix":"// before (extension getOperators infix array)\n'**' => ['precedence' => 200, 'class' => PowExpression::class, 'associativity' => 0],\n// after\n'**' => ['precedence' => 200, 'class' => PowExpression::class, 'associativity' => 2],","handlingStrategy":"validation","validationCode":"foreach ($operators[1] as $operator => $op) {\n    if (!isset($op['associativity']) || !in_array($op['associativity'], [1, 2], true)) {\n        throw new \\InvalidArgumentException(sprintf('Operator \"%s\": associativity must be 1 (left) or 2 (right), got %s.', $operator, var_export($op['associativity'] ?? null, true)));\n    }\n}","typeGuard":"function isValidAssociativity(mixed $v): bool { return $v === 1 || $v === 2; }","tryCatchPattern":null,"preventionTips":["Use the InfixAssociativity enum directly instead of legacy integers when your Twig version supports it.","Run your extension's registration in a test that constructs the Environment before shipping.","Never assume 0 means non-associative; the accepted codes are only 1 and 2.","Pin third-party Twig extensions to Twig versions their operator definitions support."],"tags":["php","twig","extension","configuration","operator-precedence"],"backgroundTag":"invalid-enum-value","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}