twigphp/Twig · error · SyntaxError

The argument " " in macro " " cannot be defined because the…

Error message

The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.

What it means

Macro definitions reserve the varargs name (internally a reserved-prefixed identifier) for collecting arbitrary extra arguments. Defining an explicit macro argument with that reserved name is rejected at compile time.

Solutions

  1. Rename the argument to something other than 'varargs'
  2. Use the implicit varargs behavior: extra arguments are already available via _varargs inside the macro
  3. If you need named variadic support, check macro variadic options in your Twig version

Example fix

// before
{% macro input(varargs) %}...{% endmacro %}
// after
{% macro input(attrs) %}...{% endmacro %}
Defensive patterns

Strategy: validation

Validate before calling

if (in_array(strtolower($argName), ['varargs'], true)) {
    throw new InvalidArgumentException("'varargs' is reserved in macros");
}

Try / catch

try {
    $twig->load('macros.html');
} catch (\Twig\Error\SyntaxError $e) {
    // macro argument problems surface at compile/load time
}

Prevention

When it happens

Trigger: Declaring {% macro foo(varargs) %} (Twig rewrites varargs with the reserved prefix) so the argument key equals TempNameExpression::RESERVED_NAME_PREFIX + 'varargs'.

Common situations: Naming a macro parameter 'varargs' believing it captures extra args like PHP's variadics; migrating code between macro implementations.

Related errors


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

Appendix: source

Thrown at src/Node/MacroNode.php:65

        if (!$body instanceof BodyNode) {
            trigger_deprecation('twig/twig', '3.12', \sprintf('Not passing a "%s" instance as the "body" argument of the "%s" constructor is deprecated ("%s" given).', BodyNode::class, static::class, $body::class));
        }

        if (!$arguments instanceof ArrayExpression) {
            trigger_deprecation('twig/twig', '3.15', \sprintf('Not passing a "%s" instance as the "arguments" argument of the "%s" constructor is deprecated ("%s" given).', ArrayExpression::class, static::class, $arguments::class));

            $args = new ArrayExpression([], $arguments->getTemplateLine());
            foreach ($arguments as $n => $default) {
                $args->addElement($default, new LocalVariable($n, $default->getTemplateLine()));
            }
            $arguments = $args;
        }

        $seen = [];
        foreach ($arguments->getKeyValuePairs() as $pair) {
            $argName = $pair['key']->getAttribute('name');
            if (TempNameExpression::RESERVED_NAME_PREFIX.self::VARARGS_NAME === $argName) {
                throw new SyntaxError(\sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $pair['value']->getTemplateLine(), $pair['value']->getSourceContext());
            }
            if (null !== $variadicName && $variadicName === $this->stripReservedPrefix($argName)) {
                throw new SyntaxError(\sprintf('The variadic argument "%s" in macro "%s" cannot have the same name as another argument.', $variadicName, $name), $pair['value']->getTemplateLine(), $pair['value']->getSourceContext());
            }
            if (isset($seen[$argName])) {
                throw new SyntaxError(\sprintf('Argument "%s" is defined twice for macro "%s".', $this->stripReservedPrefix($argName), $name), $pair['value']->getTemplateLine(), $pair['value']->getSourceContext());
            }
            $seen[$argName] = true;
        }

        parent::__construct(['body' => $body, 'arguments' => $arguments], ['name' => $name, 'variadic_name' => $variadicName], $lineno);
    }

    public function compile(Compiler $compiler): void
    {
        // 4.0 cleanup: only an explicitly declared variadic ("...name") gets a trailing
        // "...$bucket" parameter and a context entry; a non-variadic macro must NOT emit
        // "...$varargs" anymore (so extra arguments raise an error), and the implicit

View on GitHub (pinned to a414c3a491)