twigphp/Twig · error · RuntimeError
The "reduce" filter expects a sequence or a mapping, got
Error message
The "reduce" filter expects a sequence or a mapping, got "%s".
What it means
The Twig `reduce` filter (CoreExtension::reduce, called via twig_array_reduce) folds an iterable into a single value using an arrow function. Twig throws this RuntimeError when the input is not a sequence or mapping (i.e. `is_iterable()` fails). The check happens at runtime in PHP, so only affected code paths surface it.
Solutions
- Default the input to an empty array: `{{ (items ?? [])|reduce((acc, v) => acc + v, 0) }}`.
- Ensure the upstream code always returns an array (e.g. `?? []` in PHP, or fix the repository method).
- Branch on `{% if items is iterable %}` when the shape can legitimately differ.
- Catch Twig\Error\RuntimeError around Template::render to capture the failing template and line.
Example fix
// before (Twig)
{{ amounts|reduce((carry, a) => carry + a, 0) }}
// after
{{ (amounts ?? [])|reduce((carry, a) => carry + a, 0) }} Defensive patterns
Strategy: validation
Validate before calling
// PHP, before rendering
def ensure_iterable_for_reduce($value): void {
if (!is_iterable($value)) {
throw new InvalidArgumentException('reduce() input must be iterable, got ' . get_debug_type($value));
}
} Type guard
function isIterable($v): bool { return is_array($v) || $v instanceof \Traversable; } Try / catch
try {
$html = $twig->render('tpl.html.twig', $context);
} catch (\Twig\Error\RuntimeError $e) {
if (str_contains($e->getMessage(), '"reduce" filter expects a sequence or a mapping')) {
// use a zero-value default instead
} else { throw $e; }
} Prevention
- Provide an explicit `$initial` to reduce so empty arrays still yield a sane result
- Default nullable aggregates: `(items ?? [])|reduce(...)`
- Enforce collection return types (array) in services feeding templates
- Lint templates in CI with realistic empty-state fixtures
When it happens
Trigger: `{{ items|reduce((acc, v) => acc + v, 0) }}` where items is null/scalar. Also when a nullable field is passed directly and happens to be null. Guard at CoreExtension.php:2122.
Common situations: Aggregating totals over a collection that is missing/null (no rows fetched, optional relation not loaded), config scalars passed into aggregation chains, functions returning null on empty results instead of arrays.
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 "find" filter expects a sequence or a mapping, got
- The "map" filter expects a sequence or a mapping, got
- The "batch" filter expects a sequence or a mapping, got
- The "has some" test expects a sequence or a mapping, got
- The "has every" test expects a sequence or a mapping, got
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/069cbdef65680808.
Report an issue: GitHub.
Appendix: source
Thrown at src/Extension/CoreExtension.php:2122
self::checkArrow($isSandboxed, $arrow, 'map', 'filter');
$r = [];
foreach ($array as $k => $v) {
$r[$k] = $arrow($v, $k);
}
return $r;
}
/**
* @param \Closure $arrow
*
* @internal
*/
public static function reduce(Environment $env, bool $isSandboxed, $array, $arrow, $initial = null)
{
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The "reduce" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));
}
self::checkArrow($isSandboxed, $arrow, 'reduce', 'filter');
$accumulator = $initial;
foreach ($array as $key => $value) {
$accumulator = $arrow($accumulator, $value, $key);
}
return $accumulator;
}
/**
* @param \Closure $arrow
*
* @internal
*/
public static function arraySome(Environment $env, $array, $arrow, bool $isSandboxed = false)View on GitHub (pinned to a414c3a491)