{"record":{"id":"6ffbfb02323f94cb","repo":"twigphp/Twig","slug":"argument-s-could-not-be-assigned-for-s-s-s-because-it-is","errorCode":null,"errorMessage":"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\".","messagePattern":"Argument \"(.+?)\" could not be assigned for (.+?) \"(.+?)\\((.+?)\\)\" because it is mapped to an internal PHP function which cannot determine default value for optional argument(.+?) \"(.+?)\"\\.","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/Node/Expression/CallExpression.php","lineNumber":203,"sourceCode":"        foreach ($callableParameters as $callableParameter) {\n            $name = $this->normalizeName($callableParameter->name);\n            if (\\PHP_VERSION_ID >= 80000 && 'range' === $callable) {\n                if ('start' === $name) {\n                    $name = 'low';\n                } elseif ('end' === $name) {\n                    $name = 'high';\n                }\n            }\n\n            $names[] = $name;\n\n            if (\\array_key_exists($name, $parameters)) {\n                if (\\array_key_exists($pos, $parameters)) {\n                    throw new SyntaxError(\\sprintf('Argument \"%s\" is defined twice for %s \"%s\".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());\n                }\n\n                if (\\count($missingArguments)) {\n                    throw new SyntaxError(\\sprintf(\n                        '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\".',\n                        $name, $callType, $callName, implode(', ', $names), \\count($missingArguments) > 1 ? 's' : '', implode('\", \"', $missingArguments)\n                    ), $this->getTemplateLine(), $this->getSourceContext());\n                }\n\n                $arguments = array_merge($arguments, $optionalArguments);\n                $arguments[] = $parameters[$name];\n                unset($parameters[$name]);\n                $optionalArguments = [];\n            } elseif (\\array_key_exists($pos, $parameters)) {\n                $arguments = array_merge($arguments, $optionalArguments);\n                $arguments[] = $parameters[$pos];\n                unset($parameters[$pos]);\n                $optionalArguments = [];\n                ++$pos;\n            } elseif ($callableParameter->isDefaultValueAvailable()) {\n                $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), -1);\n            } elseif ($callableParameter->isOptional()) {","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Node/Expression/CallExpression.php#L185-L221","documentation":"When a named argument targets an optional parameter of a PHP internal function (extension/built-in), Twig cannot introspect that function's default values via reflection, so it requires that earlier optional parameters be explicitly filled first. If optional arguments were skipped ($missingArguments is non-empty) when trying to assign the named one, compilation fails with this SyntaxError.","triggerScenarios":"Calling a mapped PHP internal function (e.g. from an extension like twig's core functions using native PHP functions) with a named argument for an optional parameter while skipping a preceding optional parameter, e.g. {{ substr(haystack, offset=1) }} style calls where an earlier optional arg is missing.","commonSituations":"Named-argument usage on filters/functions backed by native PHP functions (str* functions, etc.); PHP version changes that alter which functions are introspectable; templates ported from userland callables to internal ones.","solutions":["Provide all preceding optional arguments positionally before the named one","Avoid named arguments for functions backed by PHP internals; pass all arguments positionally","Wrap the internal function in a custom Twig function with explicit parameter defaults","Check the mapped PHP function's signature and fill gaps"],"exampleFix":"// before (template)\n{{ substr('hello', start=2) }}\n// after\n{{ substr('hello', 0, 2) }}","handlingStrategy":"validation","validationCode":"$ref = new \\ReflectionFunction('substr');\nforeach ($ref->getParameters() as $p) {\n    if ($p->isOptional() && $p->getDefaultValue() === null && !$p->isDefaultValueAvailable()) {\n        // internal function: fill all preceding optional args explicitly\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $twig->render($template, $ctx);\n} catch (\\Twig\\Error\\SyntaxError $e) {\n    if (str_contains($e->getMessage(), 'cannot determine default value')) {\n        // rewrite call to use positional args\n    }\n}","preventionTips":["Use only positional arguments for filters/functions backed by PHP internals","Prefer custom Twig wrappers with explicit defaults over mapping raw PHP functions","Test templates against the target PHP version"],"tags":["twig","named-arguments","php-internals","reflection-limitation"],"backgroundTag":"missing-required-argument","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"}