twigphp/Twig · error · InvalidArgumentException
" ::getOperators()" must return an array with operators…
Error message
"%s::getOperators()" must return an array with operators, got "%s".
What it means
During extension initialization Twig calls getOperators() and requires an array; this InvalidArgumentException means a non-array was returned. getOperators() must return an array of exactly two elements: [unaryOperators, binaryOperators]. The message includes the actual debug type (and value for scalars) to pinpoint the bad return.
Solutions
- Make getOperators() return exactly two arrays: return [$unaryOperators, $binaryOperators];
- Add an explicit array return type (public function getOperators(): array) so PHP rejects null/non-array returns early.
- Inspect what the extension actually returns (get_debug_type) and fix the offending return statement.
- Remove or upgrade the offending third-party extension to a version compatible with your Twig.
Example fix
// before
class MyExtension extends AbstractExtension {
public function getOperators() { return ['~' => [...]]; } // flat map, not 2-element array
}
// after
class MyExtension extends AbstractExtension {
public function getOperators(): array {
return [
['not' => ['precedence' => 50, 'class' => \Twig\Node\Expression\Unary\NotUnary::class]],
['~' => ['precedence' => 30, 'class' => \Twig\Node\Expression\Binary\ConcatBinary::class]],
];
}
} Defensive patterns
Strategy: validation
Validate before calling
$ops = $ext->getOperators();
if (!\is_array($ops)) {
throw new \UnexpectedValueException(get_class($ext).'::getOperators() must return [unary, binary] arrays, got '.get_debug_type($ops));
} Type guard
function isArrayOperators(mixed $ops): bool {
return \is_array($ops);
} Try / catch
try {
$twig->addExtension($ext);
$twig->loadTemplate('any.twig');
} catch (\InvalidArgumentException $e) {
if (str_contains($e->getMessage(), '::getOperators()')) {
// inspect and fix the extension's getOperators() return value
}
throw $e;
} Prevention
- Return exactly [unaryOperators, binaryOperators] from getOperators()
- Add an explicit array return type: public function getOperators(): array
- Test extension initialization in CI so a bad getOperators() is caught before production
- Verify third-party extension compatibility with your Twig major version before upgrading
When it happens
Trigger: An extension's getOperators() returns null, false, a string, a Generator, or any non-array — e.g. returning the result of a helper that failed, returning a single merged operator array as an object, or an override without a return-type declaration silently returning null.
Common situations: Legacy extensions written for very old Twig APIs; hand-written extensions returning a single operator array instead of [unary, binary]; misconfigured third-party extensions after a Twig upgrade; overridden getOperators() with a code path that falls through without returning.
Related errors
- getTokenParsers() must return an array of…
- " ::getOperators()" must return an array of 2 elements, got…
- Unable to add a token parser as extensions have already…
- Unable to add test " " as extensions have already been…
- The "format_list" filter requires the "IntlListFormatter"…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/bfddd9c1fbee3c4a.
Report an issue: GitHub.
Appendix: source
Thrown at src/ExtensionSet.php:509
throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.');
}
$this->parsers[$parser->getTag()] = $parser;
}
// node visitors
foreach ($extension->getNodeVisitors() as $visitor) {
$this->visitors[] = $visitor;
}
// expression parsers
if (method_exists($extension, 'getExpressionParsers')) {
$this->expressionParsers->add($extension->getExpressionParsers());
}
$operators = $extension->getOperators();
if (!\is_array($operators)) {
throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array with operators, got "%s".', $extension::class, get_debug_type($operators).(\is_resource($operators) ? '' : '#'.$operators)));
}
if (2 !== \count($operators)) {
throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', $extension::class, \count($operators)));
}
$expressionParsers = [];
foreach ($operators[0] as $operator => $op) {
$expressionParsers[] = new UnaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []);
}
foreach ($operators[1] as $operator => $op) {
$op['associativity'] = match ($op['associativity']) {
1 => InfixAssociativity::Left,
2 => InfixAssociativity::Right,
default => throw new \InvalidArgumentException(\sprintf('Invalid associativity "%s" for operator "%s".', $op['associativity'], $operator)),
};
if (isset($op['callable'])) {View on GitHub (pinned to a414c3a491)