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

  1. Give the custom engine a unique, non-reserved id (avoid the 'junit-' prefix and the well-known ids).
  2. If you actually need the official engine, depend on the real artifact instead of a fork/stub.
  3. 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

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

Related errors


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)