twigphp/Twig · error · RuntimeError

The "filter" filter expects a sequence/mapping or…

Error message

The "filter" filter expects a sequence/mapping or "Traversable", got "%s".

What it means

The Twig 'filter' filter (CoreExtension::filter) applies an arrow function to a sequence/mapping or Traversable. It throws this RuntimeError when the input is not iterable, reporting the actual debug type of the value.

Solutions

  1. Default the value to an array: {{ (items ?? [])|filter(a => a.active) }}
  2. Ensure the variable in the context is an array or implements Traversable (e.g. Doctrine Collection does)
  3. Guard with {% if items is iterable %} or the 'iterable' test before filtering

Example fix

// before
{{ items|filter(i => i.enabled) }}

// after
{{ (items ?? [])|filter(i => i.enabled) }}
Defensive patterns

Strategy: validation

Validate before calling

if (!is_iterable($items)) { $items = []; }
// or in template: {% if items is iterable %}{{ items|filter(i => i.active) }}{% endif %}

Type guard

function isFilterable($v): bool { return is_array($v) || $v instanceof \Traversable; }

Try / catch

try { $out = $twig->render($tpl, $ctx); } catch (\Twig\Error\RuntimeError $e) { if (str_contains($e->getMessage(), '"filter" filter')) { /* coerce input to array/Traversable */ } }

Prevention

When it happens

Trigger: Using {{ value|filter(a => a.active) }} where value is null, a scalar, or a non-Traversable object — e.g. a null result from a repository or missing context variable.

Common situations: Passing a single entity instead of a collection; null from a failed lookup or unset context key; custom data objects that are not IteratorAggregate; strict_variables off hiding the null until the filter runs.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/ba0d52e4478077fe. Report an issue: GitHub.

Appendix: source

Thrown at src/Extension/CoreExtension.php:2058

                    if (null !== $index) {
                        $policy->checkPropertyAllowed($item, (string) $index);
                    }
                }
            }
        }

        return array_column($array, $name, $index);
    }

    /**
     * @param \Closure $arrow
     *
     * @internal
     */
    public static function filter(Environment $env, bool $isSandboxed, $array, $arrow)
    {
        if (!is_iterable($array)) {
            throw new RuntimeError(\sprintf('The "filter" filter expects a sequence/mapping or "Traversable", got "%s".', get_debug_type($array)));
        }

        self::checkArrow($isSandboxed, $arrow, 'filter', 'filter');

        if (\is_array($array)) {
            return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
        }

        // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
        return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
    }

    /**
     * @param \Closure $arrow
     *
     * @internal
     */
    public static function find(Environment $env, bool $isSandboxed, $array, $arrow)

View on GitHub (pinned to a414c3a491)