{"record":{"id":"aae64ee5e85187f6","repo":"junit-team/junit5","slug":"filter-s-must-implement-s-s-or-s","errorCode":null,"errorMessage":"Filter [%s] must implement %s, %s, or %s.","messagePattern":"Filter \\[(.+?)\\] must implement (.+?), (.+?), or (.+?)\\.","errorType":"validation","errorClass":"PreconditionViolationException","httpStatus":null,"severity":"error","filePath":"junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java","lineNumber":369,"sourceCode":"\t@API(status = MAINTAINED, since = \"1.14\")\n\tpublic LauncherDiscoveryRequestBuilder outputDirectoryCreator(OutputDirectoryCreator outputDirectoryCreator) {\n\t\tthis.outputDirectoryCreator = Preconditions.notNull(outputDirectoryCreator,\n\t\t\t\"outputDirectoryCreator must not be null\");\n\t\treturn this;\n\t}\n\n\tprivate void storeFilter(Filter<?> filter) {\n\t\tif (filter instanceof EngineFilter engineFilter) {\n\t\t\tthis.engineFilters.add(engineFilter);\n\t\t}\n\t\telse if (filter instanceof PostDiscoveryFilter postDiscoveryFilter) {\n\t\t\tthis.postDiscoveryFilters.add(postDiscoveryFilter);\n\t\t}\n\t\telse if (filter instanceof DiscoveryFilter<?> discoveryFilter) {\n\t\t\tthis.discoveryFilters.add(discoveryFilter);\n\t\t}\n\t\telse {\n\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\"Filter [%s] must implement %s, %s, or %s.\".formatted(filter, EngineFilter.class.getSimpleName(),\n\t\t\t\t\tPostDiscoveryFilter.class.getSimpleName(), DiscoveryFilter.class.getSimpleName()));\n\t\t}\n\t}\n\n\t/**\n\t * Builds this discovery request and returns a new builder for creating a\n\t * {@link org.junit.platform.launcher.LauncherExecutionRequest} that is\n\t * initialized to contain the resulting discovery request.\n\t *\n\t * @return a new {@link LauncherExecutionRequestBuilder}\n\t * @since 6.0\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic LauncherExecutionRequestBuilder forExecution() {\n\t\treturn LauncherExecutionRequestBuilder.request(build());\n\t}\n","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java#L351-L387","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic class MyFilter implements Filter<TestDescriptor> { ... } // no marker -> throws\nrequest().filters(new MyFilter());\n// after\npublic class MyFilter implements PostDiscoveryFilter { ... }\nrequest().filters(new MyFilter());","handlingStrategy":"type-guard","validationCode":"// Ensure the filter implements one of the three supported interfaces\nFilter<?> f = myFilter;\nboolean ok = f instanceof EngineFilter || f instanceof PostDiscoveryFilter || f instanceof DiscoveryFilter;\nif (!ok) throw new IllegalArgumentException(\"filter type not supported: \" + f.getClass());","typeGuard":"static boolean isSupportedFilter(Filter<?> f) {\n    return f instanceof EngineFilter || f instanceof PostDiscoveryFilter || f instanceof DiscoveryFilter;\n}","tryCatchPattern":"try {\n    builder.filters(myFilter).build();\n} catch (PreconditionViolationException e) {\n    // message names which interface to implement - update the filter class\n}","preventionTips":["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."],"tags":["filter","precondition","discovery","type-error"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}