junit-team/junit5 · error · JUnitException
Third-party TestEngine
Error message
Third-party TestEngine '%s' is forbidden to use the reserved '%s' TestEngine ID.
What it means
EngineIdValidator.validateWellKnownClassName throws if a TestEngine uses a reserved well-known id (junit-jupiter, junit-vintage, junit-platform-suite) but its implementing class is not the expected built-in class. This blocks third-party engines from impersonating the official engines via id squatting.
Solutions
- Give the custom engine a unique, non-reserved id (avoid the 'junit-' prefix and the well-known ids).
- If you actually need the official engine, depend on the real artifact instead of a fork/stub.
- For test doubles, use an id like 'test-fake-engine' rather than a reserved id.
Example fix
// before
public class MyEngine implements TestEngine {
public String getId() { return "junit-jupiter"; } // reserved -> throws
}
// after
public class MyEngine implements TestEngine {
public String getId() { return "my-custom-engine"; }
} Defensive patterns
Strategy: validation
Validate before calling
// Reject reserved ids for custom engines at authoring time
private static final Set<String> RESERVED = Set.of("junit-jupiter","junit-vintage","junit-platform-suite");
String id = myEngine.getId();
if (RESERVED.contains(id) && !wellKnownClassNameFor(id).equals(myEngine.getClass().getName()))
throw new IllegalArgumentException("reserved engine id: " + id); Type guard
static boolean isReservedId(String id) {
return Set.of("junit-jupiter","junit-vintage","junit-platform-suite").contains(id);
} Try / catch
try {
LauncherFactory.create();
} catch (JUnitException e) {
if (e.getMessage().contains("forbidden to use the reserved")) {
// rename the custom engine id and recreate
}
} Prevention
- Custom engines must not use the 'junit-' prefix or the well-known ids.
- Use a distinctive id for forks/stubs (e.g. 'mycompany-test-engine').
- For test doubles, avoid impersonating official engines.
When it happens
Trigger: A TestEngine returns 'junit-jupiter', 'junit-vintage', or 'junit-platform-suite' from getId() but its class name is not org.junit.jupiter.engine.JupiterTestEngine / org.junit.vintage.engine.VintageTestEngine / org.junit.platform.suite.engine.SuiteTestEngine respectively.
Common situations: A custom/forked engine deliberately reusing an official id; a test fixture/fake engine stub that hardcodes 'junit-jupiter' as its id; a renamed/shaded copy of an official engine whose class name no longer matches the expected one; mocking frameworks that create TestEngine proxies with a fixed id.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Cannot create Launcher for multiple engines with the same ID
- Configuration error: .
- configuration errors:%n
- @DefaultLocale not configured correctly. When not using a…
- Invalid LauncherPhase
AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11).
Data as JSON: /api/errors/32c794f2108becda.
Report an issue: GitHub.
Appendix: source
Thrown at junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java:82
}
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()));
return switch (engineId) {
case "junit-jupiter" -> "org.junit.jupiter.engine.JupiterTestEngine";
case "junit-vintage" -> "org.junit.vintage.engine.VintageTestEngine";
case "junit-platform-suite" -> "org.junit.platform.suite.engine.SuiteTestEngine";
default -> null;
};
}
private static void validateWellKnownClassName(TestEngine testEngine, String expectedClassName) {
String actualClassName = testEngine.getClass().getName();
if (actualClassName.equals(expectedClassName)) {
return;
}
throw new JUnitException(
"Third-party TestEngine '%s' is forbidden to use the reserved '%s' TestEngine ID.".formatted(
actualClassName, testEngine.getId()));
}
}
View on GitHub (pinned to f070c699a0)