junit-team/junit5 · error · ParameterResolutionException
Discovered multiple competing ParameterResolvers for…
Error message
Discovered multiple competing ParameterResolvers for parameter [%s] in %s [%s]: %s
What it means
Thrown as ParameterResolutionException when two or more ParameterResolver extensions report supportsParameter(...) == true for the same parameter. JUnit refuses to guess which resolver wins and aborts the invocation listing every competing resolver.
Solutions
- Make the custom resolver's supportsParameter more specific so it stops claiming parameters owned by other resolvers (narrow by annotation, type, position).
- Disable one of the competing resolvers via junit.jupiter.execution.extensions.enabled.extension.types filtering.
- Remove the duplicate registration (e.g. @ExtendWith present on both the class and a base class).
Example fix
// before
class WideResolver implements ParameterResolver {
public boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {
return true; // claims everything, conflicts with built-ins
}
}
// after
class MyServiceResolver implements ParameterResolver {
public boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {
return pc.getParameter().getType() == MyService.class;
}
} Defensive patterns
Strategy: validation
Validate before calling
long count = Stream.of(MyTest.class, MyTest.class.getSuperclass())
.flatMap(c -> Stream.of(c.getAnnotations()))
.filter(a -> a instanceof ExtendWith)
.flatMap(a -> Stream.of(((ExtendWith) a).value()))
.filter(c -> ParameterResolver.class.isAssignableFrom(c))
.count();
if (count > 1) throw new IllegalStateException("multiple ParameterResolvers detected"); Prevention
- Narrow custom ParameterResolver.supportsParameter to a single type/annotation/position.
- Avoid duplicating @ExtendWith on class and base class.
- Use junit.jupiter.execution.extensions.enabled.extension.types to disable overlapping resolvers.
When it happens
Trigger: matchingResolvers.size() > 1 after filtering the extension registry - e.g. two extensions both claim the same parameter type. The message enumerates them via StringUtils.defaultToString for each.
Common situations: Two libraries both register a resolver for the same type (e.g. a generic Object resolver, two TestInfo-style extensions); a developer writes a custom resolver that overlaps with a built-in (TestInfo, TempDir) one; duplicate @ExtendWith on class and field.
Related errors
- Failed to resolve parameter
- No ParameterResolver registered for parameter
- ParameterResolver [ ] resolved a value of type [ ] for…
- Can only resolve @TempDir
- Chain of InvocationInterceptors called invocation multiple…
AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11).
Data as JSON: /api/errors/e129b7d9771f95bc.
Report an issue: GitHub.
Appendix: 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 f070c699a0)