twigphp/Twig · error · SyntaxError

Positional arguments cannot be used after named arguments…

Error message

Positional arguments cannot be used after named arguments for %s "%s".

What it means

During template compilation, CallableArgumentsExtractor::extractArguments() validates the argument order of calls to Twig functions, filters, and tests. If a positional argument appears after a named one, a SyntaxError is thrown because PHP cannot bind arguments that way and Twig cannot map them deterministically. The error is raised at compile time with the offending template line.

Solutions

  1. Reorder the call so positional arguments precede named arguments.
  2. Give the positional argument its name so the entire call is named-argument based.
  3. If the callable supports it, check the documented signature for the correct parameter order.

Example fix

{# before #}
{{ date(format='Y-m-d', 'now') }}
{# after #}
{{ date('now', format='Y-m-d') }}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    $template->render($context);
} catch (Twig\Error\SyntaxError $e) {
    if (str_contains($e->getMessage(), 'Positional arguments cannot be used after named arguments')) {
        // point CI/dev at $e->getTemplateLine() in $e->getSourceContext()->getName()
        log($e->getMessage().' in '.$e->getSourceContext()->getName().' line '.$e->getTemplateLine());
    }
    throw $e;
}

Prevention

When it happens

Trigger: A template calls a function/filter/test (e.g. {{ date(format='Y', 'now') }}) with an integer-keyed argument following a string-keyed (named) argument in the node's argument list.

Common situations: Adding a named option in the middle of an existing positional call; code-generated templates that append named options before remaining positional values; copy-pasted calls reordered by hand.

Related errors


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

Appendix: source

Thrown at src/Util/CallableArgumentsExtractor.php:49

        private Node $node,
        private TwigCallableInterface $twigCallable,
    ) {
        $this->rc = new ReflectionCallable($twigCallable);
    }

    /**
     * @return array<Node>
     */
    public function extractArguments(Node $arguments): array
    {
        $extractedArguments = [];
        $extractedArgumentNameMap = [];
        $named = false;
        foreach ($arguments as $name => $node) {
            if (!\is_int($name)) {
                $named = true;
            } elseif ($named) {
                throw new SyntaxError(\sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext());
            }

            $extractedArguments[$normalizedName = $this->normalizeName($name)] = $node;
            $extractedArgumentNameMap[$normalizedName] = $name;
        }

        if (!$named && !$this->twigCallable->isVariadic()) {
            $min = $this->twigCallable->getMinimalNumberOfRequiredArguments();
            if (\count($extractedArguments) < $this->rc->getReflector()->getNumberOfRequiredParameters() - $min) {
                $argName = $this->toSnakeCase($this->rc->getReflector()->getParameters()[$min + \count($extractedArguments)]->getName());

                throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $argName, $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext());
            }

            return $extractedArguments;
        }

        if (!$callable = $this->twigCallable->getCallable()) {

View on GitHub (pinned to a414c3a491)