twigphp/Twig · error · RuntimeError

Variables passed to the "with" tag must be a mapping.

Error message

Variables passed to the "with" tag must be a mapping.

What it means

The {% with %} tag's compiled code evaluates the passed variables and requires an iterable mapping before converting it via CoreExtension::toArray(). Scalars like strings or integers passed to with are rejected at runtime with this RuntimeError.

Solutions

  1. Pass a hash: {% with { foo: bar } %}...{% endwith %}
  2. Guard the value: only enter the with-block when the variable is an array
  3. Fix upstream data so the variable is a mapping

Example fix

// before
{% with settings_string %}...{% endwith %}
// after
{% with { settings: settings_string } %}...{% endwith %}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!is_iterable($vars) && !is_array($vars)) {
    throw new InvalidArgumentException('{% with %} requires a mapping');
}

Type guard

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

Try / catch

try {
    $twig->render($template, $context);
} catch (\Twig\Error\RuntimeError $e) {
    if (str_contains($e->getMessage(), 'must be a mapping')) {
        // coerce/fix the variable passed to {% with %}
    }
    throw $e;
}

Prevention

When it happens

Trigger: {% with foo %}...{% endwith %} or {% with someString %} where the expression is not an array/object (e.g. a string, number, or boolean).

Common situations: Passing a scalar by mistake instead of a hash; a variable expected to be an array being null/string due to upstream data issues.

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/5d4e62d2228cb278. Report an issue: GitHub.

Appendix: source

Thrown at src/Node/WithNode.php:52

    public function compile(Compiler $compiler): void
    {
        $compiler->addDebugInfo($this);

        $parentContextName = $compiler->getVarName();

        $compiler->write(\sprintf("\$%s = \$context;\n", $parentContextName));

        if ($this->hasNode('variables')) {
            $node = $this->getNode('variables');
            $varsName = $compiler->getVarName();
            $compiler
                ->write(\sprintf('$%s = ', $varsName))
                ->subcompile($node)
                ->raw(";\n")
                ->write(\sprintf("if (!is_iterable(\$%s)) {\n", $varsName))
                ->indent()
                ->write("throw new RuntimeError('Variables passed to the \"with\" tag must be a mapping.', ")
                ->repr($node->getTemplateLine())
                ->raw(", \$this->getSourceContext());\n")
                ->outdent()
                ->write("}\n")
                ->write(\sprintf("\$%s = CoreExtension::toArray(\$%s);\n", $varsName, $varsName))
            ;

            if ($this->getAttribute('only')) {
                $compiler->write("\$context = [];\n");
            }

            $compiler->write(\sprintf("\$context = \$%s + \$context + \$this->env->getGlobals();\n", $varsName));
        }

        $compiler
            ->subcompile($this->getNode('body'))
            ->write(\sprintf("\$context = \$%s;\n", $parentContextName))
        ;

View on GitHub (pinned to a414c3a491)