getgrav/grav · error · UnexpectedValueException
{% link with x %}: x is not an array
Error message
{% link with x %}: x is not an array What it means
The {% link %} tag's compile() method emits PHP that evaluates the `with <expr>` clause into $attributes and immediately checks is_array($attributes); a non-array value throws UnexpectedValueException at template render time. The `with` clause is for HTML attributes and must be a Twig mapping (or an array-valued variable). The sibling `in` clause has its own string check, and the whole check only compiles when a file node is present.
Source
Thrown at system/src/Grav/Common/Twig/Node/TwigNodeLink.php:67
* @return void
* @throws LogicException
*/
public function compile(Compiler $compiler): void
{
$compiler->addDebugInfo($this);
if (!$this->hasNode('file')) {
return;
}
$compiler->write('$attributes = [\'rel\' => \'' . $this->getAttribute('rel') . '\'];' . "\n");
if ($this->hasNode('attributes')) {
$compiler
->write('$attributes += ')
->subcompile($this->getNode('attributes'))
->raw(';' . PHP_EOL)
->write('if (!is_array($attributes)) {' . PHP_EOL)
->indent()
->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');" . PHP_EOL)
->outdent()
->write('}' . PHP_EOL);
}
if ($this->hasNode('group')) {
$compiler
->write('$group = ')
->subcompile($this->getNode('group'))
->raw(';' . PHP_EOL)
->write('if (!is_string($group)) {' . PHP_EOL)
->indent()
->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');" . PHP_EOL)
->outdent()
->write('}' . PHP_EOL);
} else {
$compiler->write('$group = \'head\';' . PHP_EOL);
}
View on GitHub (pinned to 6040efed04)
Solutions
- Use a mapping literal: {% link 'style.css' with { rel: 'preload', as: 'style' } %}
- If using a variable, ensure it is an array: {% set attrs = {defer: true} %} then {% link 'app.css' with attrs %}
- Default null variables before the tag: {% set attrs = attrs ?? {} %}
Example fix
{% set defer = true %}
{# before: scalar passed to with #}
{% link 'app.css' with defer %}
{# after: mapping literal #}
{% link 'app.css' with { defer: true } %} Defensive patterns
Strategy: validation
Validate before calling
{# in Twig, guard the variable before the tag #}
{% set attrs = attrs is defined and attrs is iterable ? attrs : {} %}
{% link 'app.css' with attrs %} Try / catch
try { echo $twig->render($template, $data); } catch (UnexpectedValueException $e) { // report template + line from the exception trace
log_template_error($e); } Prevention
- Always write the with clause as a mapping literal: with { key: value }
- Default attribute variables with ?? {} before the tag
- Lint templates for `with` clauses that are not mappings in CI
When it happens
Trigger: {% link 'style.css' with defer %} where defer is a scalar variable; {% link 'x' with 'defer=true' %} (string instead of mapping); a `with` expression that evaluates to null because the variable was never set; passing an object instead of an array.
Common situations: Omitting the braces of the attribute mapping when handwriting the tag; variables expected to hold attribute arrays but left unset after refactors; copying tag examples from docs that show shorthand.
Related errors
- {% link in x %}: x is not a string
- {% script with x %}: x is not an array
- {% script in x %}: x is not a string
- {% {$this->tagName} with x %}: x is not an array
- {% {$this->tagName} in x %}: x is not a string
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/038445bab23af8ad.
Report an issue: GitHub.