{"record":{"id":"119f6a1e7e2a76a7","repo":"twigphp/Twig","slug":"argument-s-could-not-be-assigned-for-s-s-s-because-it-is-119f6a","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/Util/CallableArgumentsExtractor.php","lineNumber":100,"sourceCode":"            $callableParameterName = $callableParameter->name;\n            if (\\PHP_VERSION_ID >= 80000 && 'range' === $callable) {\n                if ('start' === $callableParameterName) {\n                    $callableParameterName = 'low';\n                } elseif ('end' === $callableParameterName) {\n                    $callableParameterName = 'high';\n                }\n            }\n\n            $callableParameterNames[] = $callableParameterName;\n            $normalizedCallableParameterName = $this->normalizeName($callableParameterName);\n\n            if (\\array_key_exists($normalizedCallableParameterName, $extractedArguments)) {\n                if (\\array_key_exists($pos, $extractedArguments)) {\n                    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());\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                        $callableParameterName, $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', array_map([$this, 'toSnakeCase'], $callableParameterNames)), \\count($missingArguments) > 1 ? 's' : '', implode('\", \"', $missingArguments)\n                    ), $this->node->getTemplateLine(), $this->node->getSourceContext());\n                }\n\n                $arguments = array_merge($arguments, $optionalArguments);\n                $arguments[] = $extractedArguments[$normalizedCallableParameterName];\n                unset($extractedArguments[$normalizedCallableParameterName]);\n                $optionalArguments = [];\n            } elseif (\\array_key_exists($pos, $extractedArguments)) {\n                $arguments = array_merge($arguments, $optionalArguments);\n                $arguments[] = $extractedArguments[$pos];\n                unset($extractedArguments[$pos]);\n                $optionalArguments = [];\n                ++$pos;\n            } elseif ($callableParameter->isDefaultValueAvailable()) {\n                $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), $this->node->getTemplateLine());\n            } elseif ($callableParameter->isOptional()) {","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Util/CallableArgumentsExtractor.php#L82-L118","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Supply the missing earlier optional arguments so no gaps remain.","Rewrite the call using strictly positional arguments in signature order.","In the extension, wrap the internal function in a userland closure with explicit defaults instead of registering the internal function directly."],"exampleFix":"// before\nnew TwigFilter('nl2br', 'nl2br')  // internal fn, gaps impossible to fill\n// after\nnew TwigFilter('nl2br', fn (string $s, bool $xhtml = true) => nl2br($s, $xhtml))","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $out = $twig->render($template, $ctx);\n} catch (Twig\\Error\\SyntaxError $e) {\n    if (str_contains($e->getMessage(), 'cannot determine default value')) {\n        throw new RuntimeException('Internal PHP function used as Twig callable cannot resolve skipped defaults: '.$e->getMessage(), 0, $e);\n    }\n    throw $e;\n}","preventionTips":["Do not register internal PHP functions directly as Twig callables; wrap them in closures with explicit defaults.","Avoid skipping middle optional arguments in calls to internal-function-backed filters/functions.","Test extension filters/functions with named arguments to expose default-value gaps early."],"tags":["twig","internal-function","named-arguments","compile-time"],"backgroundTag":"unsupported-operation","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"}