junit-team/junit5 · critical · JUnitException
Cannot create Launcher for multiple engines with the same ID
Error message
Cannot create Launcher for multiple engines with the same ID '%s'.
What it means
Thrown by EngineIdValidator.validate() during Launcher creation when two distinct TestEngine implementations report the same getId(). The launcher refuses to construct because engine IDs must be unique to route discovery and execution correctly. It is a JUnitException naming the duplicated ID.
Source
Thrown at junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java:50
}
static void validateReservedPrefix(TestEngine testEngine, UniqueId uniqueEngineId,
DiscoveryIssueCollector issueCollector) {
String engineId = testEngine.getId();
if (engineId.startsWith("junit-") && wellKnownClassNameForEngineId(testEngine) == null) {
var message = "Third-party TestEngine implementations are forbidden to use the reserved 'junit-' prefix for their ID";
issueCollector.issueEncountered(uniqueEngineId, DiscoveryIssue.create(WARNING, message));
}
}
static Iterable<TestEngine> validate(Iterable<TestEngine> testEngines) {
Set<String> ids = new HashSet<>();
for (TestEngine testEngine : testEngines) {
// check usage of reserved ids
validateReservedIds(testEngine);
// check uniqueness
if (!ids.add(testEngine.getId())) {
throw new JUnitException(
"Cannot create Launcher for multiple engines with the same ID '%s'.".formatted(testEngine.getId()));
}
}
return testEngines;
}
// https://github.com/junit-team/junit-framework/issues/1557
private static void validateReservedIds(TestEngine testEngine) {
var expectedClassName = wellKnownClassNameForEngineId(testEngine);
if (expectedClassName == null) {
return;
}
validateWellKnownClassName(testEngine, expectedClassName);
}
private static @Nullable String wellKnownClassNameForEngineId(TestEngine testEngine) {
String engineId = Preconditions.notBlank(testEngine.getId(),
() -> "ID for TestEngine [%s] must not be null or blank".formatted(testEngine.getClass().getName()));View on GitHub (pinned to 956246301e)
Solutions
- Run 'gradle dependencies' / 'mvn dependency:tree' and exclude the duplicate engine artifact.
- If shading, use filters to prevent duplicate ServiceLoader entries for TestEngine.
- Ensure only one version of each engine JAR is on the test runtime classpath.
- Inspect META-INF/services/org.junit.platform.engine.TestEngine in your classpath JARs for duplicates.
Example fix
// before: both junit-jupiter-engine 5.x and a relocated copy on classpath
// after (gradle)
configurations {
testRuntimeClasspath {
exclude group: 'org.junit.jupiter', module: 'junit-jupiter-engine' // for the dup
}
} Defensive patterns
Strategy: validation
Validate before calling
import org.junit.platform.engine.TestEngine;
Set<String> seen = new HashSet<>();
for (var eng : ServiceLoader.load(TestEngine.class)) {
if (!seen.add(eng.getId())) {
throw new IllegalStateException("Duplicate engine id: " + eng.getId() + " from " + eng.getClass().getName());
}
} Type guard
static boolean noDuplicateEngineIds(Iterable<TestEngine> engines) {
Set<String> ids = new HashSet<>();
for (var e : engines) if (!ids.add(e.getId())) return false;
return true;
} Prevention
- Use dependency:tree / gradle dependencies to detect duplicate engine artifacts.
- Configure shadow/shade plugins to merge or deduplicate ServiceLoader entries.
- Inspect META-INF/services/org.junit.platform.engine.TestEngine across classpath JARs.
When it happens
Trigger: Two TestEngine ServiceLoader providers on the classpath returning the same id from TestEngine.getId(). Common with shading, fat-jars, or duplicate dependencies bundling the same engine twice under different class names.
Common situations: A shaded/uber-jar that includes junit-jupiter-engine twice (e.g. direct dependency plus a transitive one with relocated classes). Conflicting third-party engines that both claim a custom ID like 'spek'. Dependency version mismatches causing two copies of an engine artifact.
Related errors
- OutputDirectoryCreator not available; probably due to unalig
- Could not find any resource(s) with name: ${this.classpathRe
- Error executing tests for engine ${getId()}
- Third-party TestEngine '%s' is forbidden to use the reserved
- Cannot override the standard style 'NONE'
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/705f6cc7e9ec1d8b.json.
Report an issue: GitHub.