twigphp/Twig · error · SyntaxError

Argument " " could not be assigned for " ( )" because it is…

Error message

Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".

What it means

When mapping arguments onto an internal PHP function, extractArguments() may not know default values for optional parameters that were skipped. If an argument name matches a skipped optional slot while earlier optional arguments are missing, Twig cannot fill the gap and throws this SyntaxError explaining the internal-PHP-function limitation.

Solutions

  1. Supply the missing earlier optional arguments so no gaps remain.
  2. Rewrite the call using strictly positional arguments in signature order.
  3. In the extension, wrap the internal function in a userland closure with explicit defaults instead of registering the internal function directly.

Example fix

// before
new TwigFilter('nl2br', 'nl2br')  // internal fn, gaps impossible to fill
// after
new TwigFilter('nl2br', fn (string $s, bool $xhtml = true) => nl2br($s, $xhtml))
Defensive patterns

Strategy: validation

Try / catch

try {
    $out = $twig->render($template, $ctx);
} catch (Twig\Error\SyntaxError $e) {
    if (str_contains($e->getMessage(), 'cannot determine default value')) {
        throw new RuntimeException('Internal PHP function used as Twig callable cannot resolve skipped defaults: '.$e->getMessage(), 0, $e);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling a Twig function/filter backed by an internal PHP function using a named argument that maps to an optional parameter while earlier optional parameters were not supplied, so Twig cannot compute their defaults.

Common situations: Wrapping PHP built-in/internal functions in custom Twig extensions and calling them with named arguments out of order; skipping middle optional parameters of internal functions like str_replace-backed filters.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at src/Util/CallableArgumentsExtractor.php:100

            $callableParameterName = $callableParameter->name;
            if (\PHP_VERSION_ID >= 80000 && 'range' === $callable) {
                if ('start' === $callableParameterName) {
                    $callableParameterName = 'low';
                } elseif ('end' === $callableParameterName) {
                    $callableParameterName = 'high';
                }
            }

            $callableParameterNames[] = $callableParameterName;
            $normalizedCallableParameterName = $this->normalizeName($callableParameterName);

            if (\array_key_exists($normalizedCallableParameterName, $extractedArguments)) {
                if (\array_key_exists($pos, $extractedArguments)) {
                    throw new SyntaxError(\sprintf('Argument "%s" is defined twice for %s "%s".', $callableParameterName, $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext());
                }

                if (\count($missingArguments)) {
                    throw new SyntaxError(\sprintf(
                        'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
                        $callableParameterName, $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', array_map([$this, 'toSnakeCase'], $callableParameterNames)), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments)
                    ), $this->node->getTemplateLine(), $this->node->getSourceContext());
                }

                $arguments = array_merge($arguments, $optionalArguments);
                $arguments[] = $extractedArguments[$normalizedCallableParameterName];
                unset($extractedArguments[$normalizedCallableParameterName]);
                $optionalArguments = [];
            } elseif (\array_key_exists($pos, $extractedArguments)) {
                $arguments = array_merge($arguments, $optionalArguments);
                $arguments[] = $extractedArguments[$pos];
                unset($extractedArguments[$pos]);
                $optionalArguments = [];
                ++$pos;
            } elseif ($callableParameter->isDefaultValueAvailable()) {
                $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), $this->node->getTemplateLine());
            } elseif ($callableParameter->isOptional()) {

View on GitHub (pinned to a414c3a491)