getgrav/grav · error · RuntimeError
Twig |map("{arrow}") is not allowed.
Error message
Twig |map("{arrow}") is not allowed. What it means
Grav's override of Twig's |map throws this RuntimeError before calling twig_array_map when the arrow is neither a \Closure nor a string, or when Utils::isDangerousFunction() flags the string (exec, system, passthru, shell_exec, popen, proc_open, pcntl_exec, assert, preg_replace, create_function, include/require, and callback-style functions). The guard exists because outside the sandbox Twig would happily call map('system', $value, $key) per element, turning a template into arbitrary command execution.
Source
Thrown at system/src/Grav/Common/Twig/Extension/GravExtension.php:2091
if ($array === null) {
$array = [];
}
return twig_array_filter($env, $array, $arrow);
}
/**
* @param Environment $env
* @param array $array
* @param callable|string $arrow
* @return array|CallbackFilterIterator
* @throws RuntimeError
*/
function mapFunc(Environment $env, $array, $arrow)
{
if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.');
}
if ($array === null) {
$array = [];
}
return twig_array_map($env, $array, $arrow);
}
/**
* @param Environment $env
* @param array $array
* @param callable|string $arrow
* @return array|CallbackFilterIterator
* @throws RuntimeError
*/
function reduceFunc(Environment $env, $array, $arrow)
{View on GitHub (pinned to 6040efed04)
Solutions
- Rewrite the callback as an arrow function: {{ items|map(v => v.title|upper) }}
- Wrap class methods instead of array callables: {{ items|map(v => MyClass::label(v)) }}
- Investigate any denylisted function name in a template as a possible injection, not just a style problem
- Initialize callback variables and never pass null to |map
Example fix
{# before: string callable #}
{{ items|map('shell_exec') }}
{# after: arrow function Closure #}
{{ items|map(v => v.title|upper) }} Defensive patterns
Strategy: validation
Validate before calling
// guard the arrow before render (mirrors mapFunc's check)
$ok = $arrow instanceof \Closure || (is_string($arrow) && !Utils::isDangerousFunction($arrow));
if (!$ok) { $arrow = fn($v) => $v; // safe default or fail fast
} Type guard
function isSafeTwigArrow(mixed $arrow): bool
{
return $arrow instanceof \Closure || (is_string($arrow) && !\Grav\Common\Utils::isDangerousFunction($arrow));
} Try / catch
use Twig\Error\RuntimeError;
try { echo $twig->render($template, $data); }
catch (RuntimeError $e) { log_template_error($e); // record template name/line for audit
} Prevention
- Always write |map callbacks as arrow functions: |map(v => expr)
- Initialize every variable used as a callback; null throws
- Ban string callables in templates via code review/CI grep for |map('
- Investigate denylisted names in templates as injection attempts
When it happens
Trigger: {{ items|map('exec') }} or another denylisted function name in any template; a null or array-callable arrow (['Class', 'method']) which fails the Closure-or-string check; legacy Twig templates using PHP function names as the map callback (e.g. |map('ucfirst') works, but any denylisted or non-string callable throws).
Common situations: Porting old themes to current Grav; copy-pasted snippets from old cookbook examples; template-injection attempts showing up in error logs; variables used as callbacks that are unexpectedly null.
Related errors
- Twig |filter("{arrow}") is not allowed.
- Twig |reduce("{arrow}") is not allowed.
- Twig |find("{arrow}") is not allowed.
- Twig |sort("{arrow}") is not allowed.
- The callable passed to the "array_group_by" filter must be a
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/a62b6bd06fd2ffe8.
Report an issue: GitHub.