{"record":{"id":"ea80eb8ee9bd02ab","repo":"twigphp/Twig","slug":"s-getoperators-must-return-an-array-of-2-elements-got-d","errorCode":null,"errorMessage":"\"%s::getOperators()\" must return an array of 2 elements, got %d.","messagePattern":"\"(.+?)::getOperators\\(\\)\" must return an array of 2 elements, got (.+?)\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/ExtensionSet.php","lineNumber":513,"sourceCode":"        }\n\n        // node visitors\n        foreach ($extension->getNodeVisitors() as $visitor) {\n            $this->visitors[] = $visitor;\n        }\n\n        // expression parsers\n        if (method_exists($extension, 'getExpressionParsers')) {\n            $this->expressionParsers->add($extension->getExpressionParsers());\n        }\n\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            }","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/ExtensionSet.php#L495-L531","documentation":"After confirming getOperators() returns an array, Twig requires exactly two elements: element [0] is unary operators, element [1] is binary operators. This InvalidArgumentException fires when the array has any other count (0, 1, 3, ...), typically because unary and binary were merged into one flat array or [unary, binary] was wrapped in an extra level.","triggerScenarios":"getOperators() returns an array with fewer or more than 2 elements — returning only the unary operators array, merging unary and binary into a single map, or returning [[unary, binary]] which counts as 1 element. Fires during initExtensions(), triggered by the first template load/render after adding the extension.","commonSituations":"Custom extensions written against outdated documentation; third-party extension incompatible with the installed Twig version; refactoring that flattened the [unary, binary] structure; returning [[unary, binary], extra] after wrapping.","solutions":["Return exactly two array elements: [unaryOperators, binaryOperators], even if one is empty ([]).","Check for extra wrapping levels: returning [[unary, binary]] counts as 1 element — flatten one level.","Copy the structure from \\Twig\\Extension\\CoreExtension::getOperators() as a reference.","Upgrade or replace the incompatible third-party extension and add an initialization smoke test in CI."],"exampleFix":"// before\nclass MyExtension extends AbstractExtension {\n    public function getOperators(): array { return [['bnot' => [...]]]; } // 1 element\n}\n\n// after\nclass MyExtension extends AbstractExtension {\n    public function getOperators(): array {\n        return [\n            ['bnot' => ['precedence' => 50, 'class' => MyUnary::class]], // unary\n            [],                                                           // binary (may be empty)\n        ];\n    }\n}","handlingStrategy":"validation","validationCode":"$ops = $ext->getOperators();\nif (!\\is_array($ops) || \\count($ops) !== 2 || !\\is_array($ops[0]) || !\\is_array($ops[1])) {\n    throw new \\UnexpectedValueException('getOperators() must return exactly [unaryOperators, binaryOperators]');\n}","typeGuard":"function hasTwoOperatorArrays(mixed $ops): bool {\n    return \\is_array($ops) && 2 === \\count($ops) && \\is_array($ops[0]) && \\is_array($ops[1]);\n}","tryCatchPattern":"try {\n    $twig->addExtension($ext);\n    $twig->loadTemplate('any.twig');\n} catch (\\InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), 'array of 2 elements')) {\n        // restructure getOperators() to the two-element [unary, binary] shape\n    }\n    throw $e;\n}","preventionTips":["Always return a two-element array from getOperators(), using [] for an empty unary or binary set","Do not wrap [unary, binary] in an extra array level; the count must be exactly 2","Copy the structure from \\Twig\\Extension\\CoreExtension::getOperators() as a template","Pin and test extension versions against your Twig version in CI"],"tags":["php","twig","extensions","type-validation"],"backgroundTag":"config-type-mismatch","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"}