{"record":{"id":"781194d3a46f4d69","repo":"twigphp/Twig","slug":"a-default-value-for-an-argument-must-be-a-constant-a-boolean","errorCode":null,"errorMessage":"A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).","messagePattern":"A default value for an argument must be a constant \\(a boolean, a string, a number, a sequence, or a mapping\\)\\.","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/TokenParser/MacroTokenParser.php","lineNumber":121,"sourceCode":"                if ($stream->nextIf(Token::PUNCTUATION_TYPE, ',') && !$stream->test(Token::PUNCTUATION_TYPE, ')')) {\n                    throw new SyntaxError(\\sprintf('The variadic argument \"%s\" in macro \"%s\" must be the last one.', $variadicName, $macroName), $token->getLine(), $stream->getSourceContext());\n                }\n\n                break;\n            }\n\n            $token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');\n            $name = new LocalVariable($token->getValue(), $token->getLine());\n            NodeDocumentation::add($name, $token);\n            if ($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) {\n                $default = $this->parser->parseExpression();\n            } else {\n                $default = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine());\n                $default->setAttribute('is_implicit', true);\n            }\n\n            if (!$this->checkConstantExpression($default)) {\n                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());\n            }\n            $arguments->addElement($default, $name);\n        }\n        $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');\n\n        return [$arguments, $variadicName];\n    }\n\n    // checks that the node only contains \"constant\" elements\n    private function checkConstantExpression(Node $node): bool\n    {\n        if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression\n            || $node instanceof NegUnary || $node instanceof PosUnary\n        )) {\n            return false;\n        }\n\n        foreach ($node as $n) {","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/TokenParser/MacroTokenParser.php#L103-L139","documentation":"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.","triggerScenarios":"Writing '{% macro m(a = some_var) %}' or '{% macro m(a = date()) %}' or '{% macro m(a = 1 + 2) %}'; checkConstantExpression fails and parseDefinition throws.","commonSituations":"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.","solutions":["Replace the dynamic default with a literal constant (e.g. null or '') and compute the real value inside the macro body","Use a conditional inside the macro: '{% if a is none %}{% set a = some_var %}{% endif %}'","Pass the dynamic value explicitly at every call site instead of defaulting it"],"exampleFix":"// before\n{% macro greet(name = app.user.name) %}\n// after\n{% macro greet(name = null) %}{% set name = name ?? app.user.name %}","handlingStrategy":"validation","validationCode":"// Keep a whitelist check for default expressions in macro docs/templates\nif (preg_match('/=\\s*(?!true|false|null|none|\\d|([\\'\\\"])[^\\'\\\"]*\\1|\\[|\\{)\\S/', $macroSignature)) {\n    // flag suspicious non-constant default for review\n}","typeGuard":null,"tryCatchPattern":"try {\n    $twig->render($template, $context);\n} catch (SyntaxError $e) {\n    if (str_contains($e->getMessage(), 'must be a constant')) {\n        // fall back to a version of the macro with literal defaults\n    }\n    throw $e;\n}","preventionTips":["Use only literals (bool, string, number, [] , {}) as macro argument defaults","Compute dynamic values inside the macro body with {% set %} / the ?? operator","Pass dynamic values explicitly at call sites"],"tags":["twig","macro","syntax-error","default-value"],"backgroundTag":"invalid-argument-value","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}