junit-team/junit5 · error · PreconditionViolationException
Filter [ ] must implement , , or .
Error message
Filter [%s] must implement %s, %s, or %s.
What it means
LauncherDiscoveryRequestBuilder.storeFilter rejects any Filter that is not an EngineFilter, a PostDiscoveryFilter, or a DiscoveryFilter. The builder sorts filters into the correct bucket; a filter implementing none of those three interfaces is a programming error since the launcher cannot apply it.
Solutions
- Make your custom filter implement org.junit.platform.launcher.PostDiscoveryFilter (for after-discovery filtering) or org.junit.platform.launcher.DiscoveryFilter (for during-discovery filtering), or use org.junit.platform.launcher.EngineFilter for engine selection.
- Use the built-in factory methods (includeEngines, excludeEngines, includeClassNamePatterns) instead of hand-rolling a Filter.
- If filtering by class/method, use the provided static helpers which return the correct subtype.
Example fix
// before
public class MyFilter implements Filter<TestDescriptor> { ... } // no marker -> throws
request().filters(new MyFilter());
// after
public class MyFilter implements PostDiscoveryFilter { ... }
request().filters(new MyFilter()); Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure the filter implements one of the three supported interfaces
Filter<?> f = myFilter;
boolean ok = f instanceof EngineFilter || f instanceof PostDiscoveryFilter || f instanceof DiscoveryFilter;
if (!ok) throw new IllegalArgumentException("filter type not supported: " + f.getClass()); Type guard
static boolean isSupportedFilter(Filter<?> f) {
return f instanceof EngineFilter || f instanceof PostDiscoveryFilter || f instanceof DiscoveryFilter;
} Try / catch
try {
builder.filters(myFilter).build();
} catch (PreconditionViolationException e) {
// message names which interface to implement - update the filter class
} Prevention
- Implement PostDiscoveryFilter, DiscoveryFilter, or extend EngineFilter for custom filters.
- Prefer the built-in filter factories (includeEngines, includeClassNamePatterns).
- Avoid raw Filter lambdas - they cannot be routed to a bucket.
When it happens
Trigger: Calling LauncherDiscoveryRequestBuilder.filters(filter) with a Filter whose runtime type implements only the base Filter interface (or some unrelated subinterface). For example, passing a raw Filter lambda, a org.junit.platform.engine.Filter where the generic bound is wrong, or a custom filter that forgot to extend the right marker interface.
Common situations: Authoring a custom filter and forgetting to implement DiscoveryFilter / PostDiscoveryFilter; passing a ClassFilter or ClassNameFilter of the wrong subtype; refactoring that widened/narrowed the filter interface; accidental raw-type usage suppressing the generics check.
Related errors
- Could not find any resource(s) with name
- Detected cycle in inner class hierarchy between
- Failed to create a java.net.URI from
- Failed to retrieve canonical path for directory
- Failed to retrieve canonical path for file
AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11).
Data as JSON: /api/errors/aae64ee5e85187f6.
Report an issue: GitHub.
Appendix: source
Thrown at junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java:369
@API(status = MAINTAINED, since = "1.14")
public LauncherDiscoveryRequestBuilder outputDirectoryCreator(OutputDirectoryCreator outputDirectoryCreator) {
this.outputDirectoryCreator = Preconditions.notNull(outputDirectoryCreator,
"outputDirectoryCreator must not be null");
return this;
}
private void storeFilter(Filter<?> filter) {
if (filter instanceof EngineFilter engineFilter) {
this.engineFilters.add(engineFilter);
}
else if (filter instanceof PostDiscoveryFilter postDiscoveryFilter) {
this.postDiscoveryFilters.add(postDiscoveryFilter);
}
else if (filter instanceof DiscoveryFilter<?> discoveryFilter) {
this.discoveryFilters.add(discoveryFilter);
}
else {
throw new PreconditionViolationException(
"Filter [%s] must implement %s, %s, or %s.".formatted(filter, EngineFilter.class.getSimpleName(),
PostDiscoveryFilter.class.getSimpleName(), DiscoveryFilter.class.getSimpleName()));
}
}
/**
* Builds this discovery request and returns a new builder for creating a
* {@link org.junit.platform.launcher.LauncherExecutionRequest} that is
* initialized to contain the resulting discovery request.
*
* @return a new {@link LauncherExecutionRequestBuilder}
* @since 6.0
*/
@API(status = EXPERIMENTAL, since = "6.0")
public LauncherExecutionRequestBuilder forExecution() {
return LauncherExecutionRequestBuilder.request(build());
}
View on GitHub (pinned to f070c699a0)