{"record":{"id":"2b9aa75007707e19","repo":"twigphp/Twig","slug":"the-variadic-argument-s-in-macro-s-cannot-have-a-default","errorCode":null,"errorMessage":"The variadic argument \"%s\" in macro \"%s\" cannot have a default value.","messagePattern":"The variadic argument \"(.+?)\" in macro \"(.+?)\" cannot have a default value\\.","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/TokenParser/MacroTokenParser.php","lineNumber":101,"sourceCode":"        $stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');\n        while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {\n            if (\\count($arguments)) {\n                $stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');\n\n                // if the comma above was a trailing comma, early exit the argument parse loop\n                if ($stream->test(Token::PUNCTUATION_TYPE, ')')) {\n                    break;\n                }\n            }\n\n            if ($stream->nextIf(Token::OPERATOR_TYPE, '...')) {\n                $token = $stream->expect(Token::NAME_TYPE, null, 'A variadic argument must be a name');\n                $variadicName = (new LocalVariable($token->getValue(), $token->getLine()))->getAttribute('name');\n                if (str_starts_with($variadicName, TempNameExpression::RESERVED_NAME_PREFIX)) {\n                    $variadicName = substr($variadicName, \\strlen(TempNameExpression::RESERVED_NAME_PREFIX));\n                }\n                if ($stream->test(Token::OPERATOR_TYPE, '=')) {\n                    throw new SyntaxError(\\sprintf('The variadic argument \"%s\" in macro \"%s\" cannot have a default value.', $variadicName, $macroName), $token->getLine(), $stream->getSourceContext());\n                }\n                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","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/TokenParser/MacroTokenParser.php#L83-L119","documentation":"When parsing a macro definition, Twig rejects a variadic argument (declared with the '...' prefix) that is given a default value like '...args = []'. Variadic arguments collect all leftover call arguments, so a default would be meaningless and is therefore a syntax error.","triggerScenarios":"Writing a macro parameter such as '{% macro foo(a, ...rest = [\"x\"]) %}'; the parser sees '=' after the variadic name in MacroTokenParser::parseDefinition and throws immediately.","commonSituations":"Copy-pasting ordinary argument syntax onto a variadic argument; misunderstanding Twig variadics after coming from PHP/JS where rest params sometimes appear with defaults in tutorials.","solutions":["Remove the '= default' from the variadic argument and rely on it capturing zero or more remaining arguments","Move the default onto a preceding normal argument, or handle the empty case inside the macro body with a conditional","Check the macro signature: only regular arguments may have defaults, and the variadic must come last"],"exampleFix":"// before\n{% macro render(items, ...attrs = []) %}\n// after\n{% macro render(items, ...attrs) %}{% if attrs is empty %}...{% endif %}","handlingStrategy":"validation","validationCode":"// Template-source check before rendering/compiling\nif (preg_match('/\\.\\.\\.\\s*\\w+\\s*=/', $templateSource)) {\n    throw new InvalidArgumentException('Variadic macro arguments must not have default values.');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $twig->parse($env->tokenize(new Source($template, 'tpl')));\n} catch (SyntaxError $e) {\n    // report $e->getMessage() with template line to the author\n}","preventionTips":["Never put '= default' after a '...name' macro argument","Give defaults only to regular (non-variadic) arguments before the variadic one","Lint macro-heavy templates in CI with lint:twig"],"tags":["twig","macro","syntax-error","template"],"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"}