junit-team/junit5 · error · PreconditionViolationException

Filter [%s] must implement %s, %s, or %s.

Error message

Filter [%s] must implement %s, %s, or %s.

What it means

Thrown by LauncherDiscoveryRequestBuilder.storeFilter() when a Filter passed to .filters(Filter<?>...) is neither an EngineFilter, a PostDiscoveryFilter, nor a DiscoveryFilter. The builder classifies each filter by instanceof and rejects unknown filter types with PreconditionViolationException naming the offending object and the three accepted interfaces.

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 956246301e)

Solutions

  1. Ensure every filter passed implements EngineFilter, PostDiscoveryFilter, or DiscoveryFilter.
  2. Use the documented factory methods (includeEngines, includeTags, includeClassNamePatterns) which return the correct subtypes.
  3. If you have a custom filter, implement PostDiscoveryFilter (most common choice) or DiscoveryFilter.

Example fix

// before
.filters(new MyRawFilter()) // MyRawFilter only implements Filter<TestDescriptor>

// after
public class MyRawFilter implements PostDiscoveryFilter { ... }
.filters(new MyRawFilter())
Defensive patterns

Strategy: type-guard

Validate before calling

import org.junit.platform.launcher.EngineFilter;
import org.junit.platform.launcher.PostDiscoveryFilter;
import org.junit.platform.engine.DiscoveryFilter;
for (Filter<?> f : filters) {
    if (!(f instanceof EngineFilter || f instanceof PostDiscoveryFilter || f instanceof DiscoveryFilter)) {
        throw new IllegalArgumentException("Unsupported filter: " + f.getClass());
    }
}

Type guard

static boolean isAcceptableFilter(Filter<?> f) {
    return f instanceof EngineFilter
        || f instanceof PostDiscoveryFilter
        || f instanceof DiscoveryFilter;
}

Prevention

When it happens

Trigger: Passing a raw Filter implementation, a tag filter wrapped incorrectly, or any custom Filter that does not extend one of the three recognised interfaces into LauncherDiscoveryRequestBuilder.filters(...).

Common situations: Passing TagFilter.includeTags(...) correctly is fine (it returns a PostDiscoveryFilter), but passing a hand-rolled Filter or a ClassNameFilter where the static import returns a DiscoveryFilter (which is accepted) vs a plain Filter. Mixing in a org.junit.platform.engine.Filter subtype that is none of the three. Wrong static import returning a generic Filter.

Related errors


AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04). Data as JSON: /data/errors/aae64ee5e85187f6.json. Report an issue: GitHub.