symfony/http-kernel · error · LogicException

You can only pin one resolver per argument, but argument "$

Error message

You can only pin one resolver per argument, but argument "$%s" of "%s()" has more.

What it means

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.

Solutions

  1. Remove all but one pinned resolver attribute from the argument.
  2. If conversion chaining is intended, ensure exactly one pinned resolver implements SourceValueResolverInterface and it is the value source.
  3. Move secondary resolution logic into a custom resolver that chains internally instead of pinning multiple.

Example fix

// before
public function show(#[CurrentUser] #[MapQueryString] User $user, #[MapDateTime] \DateTime $dt) {}
// after
public function show(#[CurrentUser] User $user, #[MapQueryString] QueryDto $q) {}
Defensive patterns

Strategy: validation

Validate before calling

$attrs = $reflectionParameter->getAttributes();
$pinned = array_filter($attrs, fn($a) => str_contains($a->getName(), 'Resolver') || in_array($a->getName(), [MapQueryParameter::class, MapRequestPayload::class, CurrentUser::class], true));
if (count($pinned) > 1) { throw new \AssertionError('Only one pinned resolver allowed per argument'); }

Try / catch

try { $args = $resolver->getArguments($request, $controller); } catch (\LogicException $e) { /* review stacked resolver attributes */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13). Data as JSON: /api/errors/8f34339404690f80. Report an issue: GitHub.

Appendix: source

Thrown at Controller/ArgumentResolver.php:74

            $disabledResolvers = [];

            if ($this->namedResolvers && $attributes = $metadata->getAttributesOfType(ValueResolver::class, $metadata::IS_INSTANCEOF)) {
                $resolverNames = [];
                foreach ($attributes as $attribute) {
                    if ($attribute->disabled) {
                        $disabledResolvers[$attribute->resolver] = true;
                    } else {
                        $resolverNames[] = $attribute->resolver;
                    }
                }

                if ($resolverNames) {
                    // Several resolvers can be pinned when exactly one of them selects the source of
                    // the value: that one runs first, the others convert what it stages.
                    $sourceNames = array_values(array_filter($resolverNames, fn ($name) => $this->namedResolvers->has($name) && $this->namedResolvers->get($name) instanceof SourceValueResolverInterface));

                    if (1 < \count($resolverNames) && 1 !== \count($sourceNames)) {
                        throw new \LogicException(\sprintf('You can only pin one resolver per argument, but argument "$%s" of "%s()" has more.', $metadata->getName(), $metadata->getControllerName()));
                    }

                    $pinnedResolvers = [];
                    foreach ([...$sourceNames, ...array_diff($resolverNames, $sourceNames)] as $resolverName) {
                        if (!$this->namedResolvers->has($resolverName)) {
                            throw new ResolverNotFoundException($resolverName, $this->namedResolvers instanceof ServiceProviderInterface ? array_keys($this->namedResolvers->getProvidedServices()) : []);
                        }

                        $pinnedResolvers[] = $this->namedResolvers->get($resolverName);
                    }

                    // A source resolver only selects where the value comes from: keep the whole chain
                    // behind it so that the resolver able to build the argument type still runs.
                    $argumentValueResolvers = $sourceNames
                        ? [...$pinnedResolvers, ...$this->argumentValueResolvers]
                        : [
                            $pinnedResolvers[0],
                            new RequestAttributeValueResolver(),

View on GitHub (pinned to aa3a39d728)