getgrav/grav · error · RuntimeError
Twig |sort("{arrow}") is not allowed.
Error message
Twig |sort("{arrow}") is not allowed. What it means
Grav's hardened |sort override refuses a comparator arrow that is neither a \Closure nor a string, or that Utils::isDangerousFunction() flags (e.g. sort('system') would be invoked as system($a, $b) outside the sandbox). A null arrow is explicitly allowed and produces a plain sort via CoreExtension::sort; the resolved sandbox state is passed through so string comparators are additionally refused in sandbox mode (GHSA-p6qj-p5m7-f62h) instead of relying on the denylist alone.
Source
Thrown at system/src/Grav/Common/Twig/Extension/GravExtension.php:2169
* Hardened `sort` filter. Same rationale as findFunc(): a string comparator
* such as `sort('system')` would otherwise be called as `system($a, $b)` when
* rendered outside the sandbox. Plain sorts (no comparator) are unaffected.
*
* The resolved sandbox state is passed through so a string comparator is refused
* in sandbox mode (GHSA-p6qj-p5m7-f62h); hardcoding it off left the denylist as
* the only guard, and the denylist does not list every two-argument callable.
*
* @param Environment $env
* @param bool $isSandboxed
* @param mixed $array
* @param callable|string|null $arrow
* @return array
* @throws RuntimeError
*/
function sortFunc(Environment $env, bool $isSandboxed, $array, $arrow = null)
{
if ($arrow !== null && (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow))) {
throw new RuntimeError('Twig |sort("' . $arrow . '") is not allowed.');
}
return CoreExtension::sort($env, $isSandboxed, $array ?? [], $arrow);
}
}
View on GitHub (pinned to 6040efed04)
Solutions
- Rewrite the comparator as an arrow function: {{ items|sort((a, b) => a.date <=> b.date) }}
- For plain value sorting, drop the comparator entirely: {{ items|sort }}
- Wrap class methods instead of array callables: {{ items|sort((a, b) => MyClass::rank(a) <=> MyClass::rank(b)) }}
- Audit the template source if a denylisted name appears — treat it as attempted injection
Example fix
{# before: string comparator #}
{{ items|sort('system') }}
{# after: arrow function comparator (or plain |sort) #}
{{ items|sort((a, b) => a.date <=> b.date) }} Defensive patterns
Strategy: validation
Validate before calling
// guard the comparator before render (mirrors sortFunc's check; null means plain sort)
$ok = $arrow === null || $arrow instanceof \Closure || (is_string($arrow) && !Utils::isDangerousFunction($arrow));
if (!$ok) { $arrow = null; // fall back to plain sort or fail fast
} Type guard
function isSafeTwigComparator(mixed $arrow): bool
{
return $arrow === null || $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); // audit any denylisted comparator name
} Prevention
- Prefer plain |sort when you don't need a custom comparator (null arrow is always allowed)
- Write comparators as arrow functions: |sort((a, b) => a <=> b)
- CI-grep for |sort(' and reject
- Audit templates containing command-function names as comparators
When it happens
Trigger: {{ items|sort('system') }} or another denylisted function name as comparator; passing an array callable like ['MyClass', 'compare']; a null/non-string non-Closure value accidentally used as the comparator (note: no comparator at all — |sort — is fine); legacy templates using usort-style string comparators.
Common situations: Sorting snippets copied from old Twig 1.x/2.x examples; injected content attempting command execution through comparator callables; refactors that leave a comparator variable null.
Related errors
- Twig |filter("{arrow}") is not allowed.
- Twig |map("{arrow}") is not allowed.
- Twig |reduce("{arrow}") is not allowed.
- Twig |find("{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/3ac688380ec7e04d.
Report an issue: GitHub.