twigphp/Twig · error · SyntaxError

A default value for an argument must be a constant (a…

Error message

A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).

What it means

Macro argument defaults must be constant expressions (literal booleans, strings, numbers, arrays/sequences, or mappings). Twig evaluates defaults at compile time, so dynamic expressions like variables, function calls, or operators as default values are rejected.

Solutions

  1. Replace the dynamic default with a literal constant (e.g. null or '') and compute the real value inside the macro body
  2. Use a conditional inside the macro: '{% if a is none %}{% set a = some_var %}{% endif %}'
  3. Pass the dynamic value explicitly at every call site instead of defaulting it

Example fix

// before
{% macro greet(name = app.user.name) %}
// after
{% macro greet(name = null) %}{% set name = name ?? app.user.name %}
Defensive patterns

Strategy: validation

Validate before calling

// Keep a whitelist check for default expressions in macro docs/templates
if (preg_match('/=\s*(?!true|false|null|none|\d|([\'\"])[^\'\"]*\1|\[|\{)\S/', $macroSignature)) {
    // flag suspicious non-constant default for review
}

Try / catch

try {
    $twig->render($template, $context);
} catch (SyntaxError $e) {
    if (str_contains($e->getMessage(), 'must be a constant')) {
        // fall back to a version of the macro with literal defaults
    }
    throw $e;
}

Prevention

When it happens

Trigger: Writing '{% macro m(a = some_var) %}' or '{% macro m(a = date()) %}' or '{% macro m(a = 1 + 2) %}'; checkConstantExpression fails and parseDefinition throws.

Common situations: Trying to default an argument to another macro argument, an environment variable, or the result of a function/filter — patterns developers carry over from PHP function signatures.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/TokenParser/MacroTokenParser.php:121

                if ($stream->nextIf(Token::PUNCTUATION_TYPE, ',') && !$stream->test(Token::PUNCTUATION_TYPE, ')')) {
                    throw new SyntaxError(\sprintf('The variadic argument "%s" in macro "%s" must be the last one.', $variadicName, $macroName), $token->getLine(), $stream->getSourceContext());
                }

                break;
            }

            $token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');
            $name = new LocalVariable($token->getValue(), $token->getLine());
            NodeDocumentation::add($name, $token);
            if ($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) {
                $default = $this->parser->parseExpression();
            } else {
                $default = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine());
                $default->setAttribute('is_implicit', true);
            }

            if (!$this->checkConstantExpression($default)) {
                throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).', $token->getLine(), $stream->getSourceContext());
            }
            $arguments->addElement($default, $name);
        }
        $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');

        return [$arguments, $variadicName];
    }

    // checks that the node only contains "constant" elements
    private function checkConstantExpression(Node $node): bool
    {
        if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression
            || $node instanceof NegUnary || $node instanceof PosUnary
        )) {
            return false;
        }

        foreach ($node as $n) {

View on GitHub (pinned to a414c3a491)