junit-team/junit5 · error · ParameterResolutionException

Discovered multiple competing ParameterResolvers for paramet

Error message

Discovered multiple competing ParameterResolvers for parameter [%s] in %s [%s]: %s

What it means

Thrown by ParameterResolutionUtils.resolveParameter() when two or more ParameterResolvers claim the same parameter (supportsParameter returns true for more than one resolver). JUnit requires a single owner per parameter to avoid ambiguity, so the conflict is reported as a ParameterResolutionException listing every competing resolver.

Source

Thrown at junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ParameterResolutionUtils.java:145

			// @formatter:off
			List<ParameterResolver> matchingResolvers = extensionRegistry.stream(ParameterResolver.class)
					.filter(resolver -> resolver.supportsParameter(parameterContext, extensionContext.get(resolver)))
					.toList();
			// @formatter:on

			if (matchingResolvers.isEmpty()) {
				throw new ParameterResolutionException(
					"No ParameterResolver registered for parameter [%s] in %s [%s].".formatted(
						parameterContext.getParameter(), asLabel(executable), executable.toGenericString()));
			}

			if (matchingResolvers.size() > 1) {
				// @formatter:off
				String resolvers = matchingResolvers.stream()
						.map(StringUtils::defaultToString)
						.collect(joining(", "));
				// @formatter:on
				throw new ParameterResolutionException(
					"Discovered multiple competing ParameterResolvers for parameter [%s] in %s [%s]: %s".formatted(
						parameterContext.getParameter(), asLabel(executable), executable.toGenericString(), resolvers));
			}

			ParameterResolver resolver = matchingResolvers.get(0);
			Object value = resolver.resolveParameter(parameterContext, extensionContext.get(resolver));
			validateResolvedType(parameterContext.getParameter(), value, executable, resolver);

			logger.trace(
				() -> "ParameterResolver [%s] resolved a value of type [%s] for parameter [%s] in %s [%s].".formatted(
					resolver.getClass().getName(), (value != null ? value.getClass().getTypeName() : null),
					parameterContext.getParameter(), asLabel(executable), executable.toGenericString()));

			return value;
		}
		catch (ParameterResolutionException ex) {
			throw ex;
		}

View on GitHub (pinned to 956246301e)

Solutions

  1. Narrow one resolver's supportsParameter() so only one claims the parameter (e.g. check an annotation or exact type).
  2. Remove the duplicate registration — search for @RegisterExtension fields and @ExtendWith meta-annotations across the class hierarchy.
  3. If using two frameworks that overlap, restrict each to distinct parameter types/annotations.

Example fix

// before — resolver too broad
public boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {
    return true; // claims everything -> conflict with other resolvers
}
// after
public boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {
    return pc.isAnnotated(MyAnnotation.class);
}
Defensive patterns

Strategy: validation

Validate before calling

// Detect multiple supporting resolvers up front
long count = registeredResolvers.stream().filter(r -> r.supportsParameter(pc, ctx)).count();
if (count > 1) throw new IllegalStateException("Competing resolvers for " + pc.getParameter());

Type guard

static boolean exactlyOneResolver(Parameter p, List<ParameterResolver> resolvers, ExtensionContext ctx) {
    ParameterContext pc = new DefaultParameterContext(p, 0, null);
    return resolvers.stream().filter(r -> r.supportsParameter(pc, ctx)).count() == 1;
}

Prevention

When it happens

Trigger: Registering two extensions whose ParameterResolvers both support the same parameter type/context — e.g. two @RegisterExtension fields that resolve the same type, or a third-party resolver overlapping with a custom one, or accidentally registering the same extension twice.

Common situations: Combining two mocking frameworks that both resolve a parameter; a custom resolver that is too broad (returns true from supportsParameter for any type); duplicate registration of @RegisterExtension via inheritance from a base class plus the subclass.

Related errors


AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04). Data as JSON: /data/errors/e129b7d9771f95bc.json. Report an issue: GitHub.