{"record":{"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":"error","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/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java#L32-L68","documentation":"EngineIdValidator.validate throws if two registered TestEngines report the same getId(). The launcher cannot disambiguate engines by id during discovery and execution, so duplicate ids are a hard error at launcher creation. The id is reported in the message.","triggerScenarios":"Two TestEngine ServiceLoader providers (or manually registered engines) return the same string from TestEngine.getId(). For example, two copies of junit-jupiter-engine on the classpath, or a custom engine whose id collides with a built-in.","commonSituations":"Duplicate dependency: junit-jupiter-engine present both as a transitive and an explicit dependency, or a shaded/fat jar bundling it twice; a custom engine hardcoded to id 'junit-jupiter' or another taken id; running with both the standalone console (which bundles engines) and explicit engine jars on the classpath.","solutions":["Inspect the classpath for duplicate engine artifacts (gradle dependencies / mvn dependency:tree) and exclude the duplicate.","If authoring a custom TestEngine, choose a unique id (not 'junit-jupiter', 'junit-vintage', 'junit-platform-suite').","When building a fat/shaded jar, use services resource merging so the same engine provider is not registered twice."],"exampleFix":"// before - duplicate engine on classpath (gradle)\ntestImplementation('org.junit.jupiter:junit-jupiter-engine')\ntestImplementation('org.junit.platform:junit-platform-console-standalone') // also bundles it\n// after - keep only one source\ntestImplementation('org.junit.jupiter:junit-jupiter')\ntestRuntimeOnly('org.junit.platform:junit-platform-launcher')","handlingStrategy":"validation","validationCode":"// At build time, detect duplicate engine providers\nSet<String> ids = new HashSet<>();\nfor (TestEngine e : ServiceLoader.load(TestEngine.class)) {\n    if (!ids.add(e.getId())) throw new IllegalStateException(\"duplicate engine id: \" + e.getId());\n}","typeGuard":"static boolean allEngineIdsUnique(Iterable<TestEngine> engines) {\n    Set<String> seen = new HashSet<>();\n    for (TestEngine e : engines) if (!seen.add(e.getId())) return false;\n    return true;\n}","tryCatchPattern":"try {\n    LauncherFactory.create();\n} catch (JUnitException e) {\n    if (e.getMessage().contains(\"multiple engines with the same ID\")) {\n        // dedupe classpath, then recreate\n    }\n}","preventionTips":["Run dependency analysis to avoid duplicate engine jars.","Configure shaded-jar plugins to merge ServiceLoader resources.","Use unique ids for custom TestEngines."],"tags":["engine-id","duplicate","classpath","serviceloader"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}