{"record":{"id":"8f34339404690f80","repo":"symfony/http-kernel","slug":"you-can-only-pin-one-resolver-per-argument-but-argument-s-of","errorCode":null,"errorMessage":"You can only pin one resolver per argument, but argument \"$%s\" of \"%s()\" has more.","messagePattern":"You can only pin one resolver per argument, but argument \"\\$(.+?)\" of \"(.+?)\\(\\)\" has more\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"Controller/ArgumentResolver.php","lineNumber":74,"sourceCode":"            $disabledResolvers = [];\n\n            if ($this->namedResolvers && $attributes = $metadata->getAttributesOfType(ValueResolver::class, $metadata::IS_INSTANCEOF)) {\n                $resolverNames = [];\n                foreach ($attributes as $attribute) {\n                    if ($attribute->disabled) {\n                        $disabledResolvers[$attribute->resolver] = true;\n                    } else {\n                        $resolverNames[] = $attribute->resolver;\n                    }\n                }\n\n                if ($resolverNames) {\n                    // Several resolvers can be pinned when exactly one of them selects the source of\n                    // the value: that one runs first, the others convert what it stages.\n                    $sourceNames = array_values(array_filter($resolverNames, fn ($name) => $this->namedResolvers->has($name) && $this->namedResolvers->get($name) instanceof SourceValueResolverInterface));\n\n                    if (1 < \\count($resolverNames) && 1 !== \\count($sourceNames)) {\n                        throw new \\LogicException(\\sprintf('You can only pin one resolver per argument, but argument \"$%s\" of \"%s()\" has more.', $metadata->getName(), $metadata->getControllerName()));\n                    }\n\n                    $pinnedResolvers = [];\n                    foreach ([...$sourceNames, ...array_diff($resolverNames, $sourceNames)] as $resolverName) {\n                        if (!$this->namedResolvers->has($resolverName)) {\n                            throw new ResolverNotFoundException($resolverName, $this->namedResolvers instanceof ServiceProviderInterface ? array_keys($this->namedResolvers->getProvidedServices()) : []);\n                        }\n\n                        $pinnedResolvers[] = $this->namedResolvers->get($resolverName);\n                    }\n\n                    // A source resolver only selects where the value comes from: keep the whole chain\n                    // behind it so that the resolver able to build the argument type still runs.\n                    $argumentValueResolvers = $sourceNames\n                        ? [...$pinnedResolvers, ...$this->argumentValueResolvers]\n                        : [\n                            $pinnedResolvers[0],\n                            new RequestAttributeValueResolver(),","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/symfony/http-kernel/blob/aa3a39d7286a62cdfea98f0e69c651a3da6e36cf/Controller/ArgumentResolver.php#L56-L92","documentation":"ArgumentResolver::getArguments() enforces that when argument attribute pinning (#[AutowireResolver]-style named resolvers) is used, at most one resolver may be pinned per argument unless exactly one of them is a SourceValueResolverInterface. A LogicException is thrown when multiple pinned resolvers are given and none/many qualify as the single source.","triggerScenarios":"Annotating a controller argument with two or more pinned resolver attributes (e.g. two #[ValueResolver] attributes or a pinned combination) where the resolvers are all converters (none is a SourceValueResolver) or more than one is a source resolver.","commonSituations":"Stacking #[CurrentUser] with #[MapRequestPayload] or similar on the same argument; copy-pasting resolver attributes; Symfony 6.1+ pinned-resolver feature used with legacy resolvers that are not source resolvers.","solutions":["Remove all but one pinned resolver attribute from the argument.","If conversion chaining is intended, ensure exactly one pinned resolver implements SourceValueResolverInterface and it is the value source.","Move secondary resolution logic into a custom resolver that chains internally instead of pinning multiple."],"exampleFix":"// before\npublic function show(#[CurrentUser] #[MapQueryString] User $user, #[MapDateTime] \\DateTime $dt) {}\n// after\npublic function show(#[CurrentUser] User $user, #[MapQueryString] QueryDto $q) {}","handlingStrategy":"validation","validationCode":"$attrs = $reflectionParameter->getAttributes();\n$pinned = array_filter($attrs, fn($a) => str_contains($a->getName(), 'Resolver') || in_array($a->getName(), [MapQueryParameter::class, MapRequestPayload::class, CurrentUser::class], true));\nif (count($pinned) > 1) { throw new \\AssertionError('Only one pinned resolver allowed per argument'); }","typeGuard":null,"tryCatchPattern":"try { $args = $resolver->getArguments($request, $controller); } catch (\\LogicException $e) { /* review stacked resolver attributes */ }","preventionTips":["Never stack two resolver attributes on the same controller argument","Check SourceValueResolverInterface semantics when chaining resolvers","Review controller signatures in code review for attribute conflicts"],"tags":["symfony","controller","argument-resolver","attributes"],"backgroundTag":"conflicting-config-options","analyzedSha":"aa3a39d7286a62cdfea98f0e69c651a3da6e36cf","analyzedAt":"2026-09-13T18:03:36.509Z","contentChangedAt":"2026-09-13T18:03:36.509Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}