twigphp/Twig · error · RuntimeError

The "find" filter expects a sequence or a mapping, got

Error message

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

What it means

The Twig `find` filter (CoreExtension::find) requires its first argument to be iterable — an array, Traversable, or object implementing iterable. Twig throws this RuntimeError when the value piped into `find` is a scalar (string, int, bool, null, etc.) instead of a sequence or mapping. It is thrown at runtime in PHP, not at template compile time, because the argument type cannot always be statically known.

Solutions

  1. Guard the value in the template before filtering: use the `default` filter or an `is iterable` test, e.g. `{{ (items ?? [])|find(...) }}`.
  2. Fix the data source so the variable actually contains an array/Traversable (check the controller/context assignment).
  3. If the value may legitimately be a scalar, branch with `{% if items is iterable %}...{% else %}...{% endif %}`.
  4. Catch Twig\Error\RuntimeError around the render call to surface the offending variable name via the template trace.

Example fix

// before (Twig)
{{ user.roles|find(role => role == 'ADMIN') }}

// after
{% if user.roles is iterable %}
  {{ user.roles|find(role => role == 'ADMIN') }}
{% endif %}
Defensive patterns

Strategy: validation

Validate before calling

// PHP, before rendering
def ensure_iterable_for_find($value): void {
    if (!is_iterable($value)) {
        throw new InvalidArgumentException('find() 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(), '"find" filter expects a sequence or a mapping')) {
        // log offending variable, render fallback
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Calling `{{ [1,2,3]|find(v => v > 1) }}` where the value before the filter is actually a scalar — e.g. `{{ some_var|find(v => v) }}` where some_var is null, a string, or an int. The guard is `if (!is_iterable($array))` at CoreExtension.php:2079.

Common situations: A variable that is usually an array is null on this code path (e.g. a missing relation or unset context key); a function returns false/null on failure instead of an array; a doctrine collection was not initialized; passing a scalar from config into a filter chain.

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


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

Appendix: source

Thrown at src/Extension/CoreExtension.php:2079

        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)
    {
        if (!is_iterable($array)) {
            throw new RuntimeError(\sprintf('The "find" filter expects a sequence or a mapping, got "%s".', get_debug_type($array)));
        }

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

        foreach ($array as $k => $v) {
            if ($arrow($v, $k)) {
                return $v;
            }
        }

        return null;
    }

    /**
     * @param \Closure $arrow
     *
     * @internal
     */

View on GitHub (pinned to a414c3a491)