{"id":"705f6cc7e9ec1d8b","repo":"junit-team/junit5","slug":"cannot-create-launcher-for-multiple-engines-with-t","errorCode":null,"errorMessage":"Cannot create Launcher for multiple engines with the same ID '%s'.","messagePattern":"Cannot create Launcher for multiple engines with the same ID '(.+?)'\\.","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"critical","filePath":"junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java","lineNumber":50,"sourceCode":"\t}\n\n\tstatic void validateReservedPrefix(TestEngine testEngine, UniqueId uniqueEngineId,\n\t\t\tDiscoveryIssueCollector issueCollector) {\n\t\tString engineId = testEngine.getId();\n\t\tif (engineId.startsWith(\"junit-\") && wellKnownClassNameForEngineId(testEngine) == null) {\n\t\t\tvar message = \"Third-party TestEngine implementations are forbidden to use the reserved 'junit-' prefix for their ID\";\n\t\t\tissueCollector.issueEncountered(uniqueEngineId, DiscoveryIssue.create(WARNING, message));\n\t\t}\n\t}\n\n\tstatic Iterable<TestEngine> validate(Iterable<TestEngine> testEngines) {\n\t\tSet<String> ids = new HashSet<>();\n\t\tfor (TestEngine testEngine : testEngines) {\n\t\t\t// check usage of reserved ids\n\t\t\tvalidateReservedIds(testEngine);\n\t\t\t// check uniqueness\n\t\t\tif (!ids.add(testEngine.getId())) {\n\t\t\t\tthrow new JUnitException(\n\t\t\t\t\t\"Cannot create Launcher for multiple engines with the same ID '%s'.\".formatted(testEngine.getId()));\n\t\t\t}\n\t\t}\n\t\treturn testEngines;\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/1557\n\tprivate static void validateReservedIds(TestEngine testEngine) {\n\t\tvar expectedClassName = wellKnownClassNameForEngineId(testEngine);\n\t\tif (expectedClassName == null) {\n\t\t\treturn;\n\t\t}\n\t\tvalidateWellKnownClassName(testEngine, expectedClassName);\n\t}\n\n\tprivate static @Nullable String wellKnownClassNameForEngineId(TestEngine testEngine) {\n\t\tString engineId = Preconditions.notBlank(testEngine.getId(),\n\t\t\t() -> \"ID for TestEngine [%s] must not be null or blank\".formatted(testEngine.getClass().getName()));","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/junit-team/junit5/blob/956246301e03d757539f7b76dd5a91f298637563/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java#L32-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: both junit-jupiter-engine 5.x and a relocated copy on classpath\n\n// after (gradle)\nconfigurations {\n    testRuntimeClasspath {\n        exclude group: 'org.junit.jupiter', module: 'junit-jupiter-engine' // for the dup\n    }\n}","handlingStrategy":"validation","validationCode":"import org.junit.platform.engine.TestEngine;\nSet<String> seen = new HashSet<>();\nfor (var eng : ServiceLoader.load(TestEngine.class)) {\n    if (!seen.add(eng.getId())) {\n        throw new IllegalStateException(\"Duplicate engine id: \" + eng.getId() + \" from \" + eng.getClass().getName());\n    }\n}","typeGuard":"static boolean noDuplicateEngineIds(Iterable<TestEngine> engines) {\n    Set<String> ids = new HashSet<>();\n    for (var e : engines) if (!ids.add(e.getId())) return false;\n    return true;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["engine","classpath","service-loader","duplicate","junit-platform"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}