apache/beam · error · IllegalArgumentException
Class '%s' does not implement PipelineRunner. Supported pipe
Error message
Class '%s' does not implement PipelineRunner. Supported pipeline runners %s
What it means
The 'runner' option value named a class that loads successfully but does not extend PipelineRunner. Beam validates the loaded Class with Class.forName and isAssignableFrom before using it, throwing IllegalArgumentException with the list of supported runners.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java:1924
}
}
Method method = propertyNamesToGetters.get(entry.getKey());
// Only allow empty argument values for String, String Array, and
// Collection<String>.
Class<?> returnType = method.getReturnType();
JavaType type = MAPPER.getTypeFactory().constructType(method.getGenericReturnType());
if ("runner".equals(entry.getKey())) {
String runner = Iterables.getOnlyElement(entry.getValue());
final Map<String, Class<? extends PipelineRunner<?>>> pipelineRunners =
cache.supportedPipelineRunners;
if (pipelineRunners.containsKey(runner.toLowerCase())) {
convertedOptions.put("runner", pipelineRunners.get(runner.toLowerCase(ROOT)));
} else {
try {
Class<?> runnerClass = Class.forName(runner, true, ReflectHelpers.findClassLoader());
if (!PipelineRunner.class.isAssignableFrom(runnerClass)) {
throw new IllegalArgumentException(
String.format(
"Class '%s' does not implement PipelineRunner. "
+ "Supported pipeline runners %s",
runner, cache.getSupportedRunners()));
}
convertedOptions.put("runner", runnerClass);
} catch (ClassNotFoundException e) {
String msg =
String.format(
"Unknown 'runner' specified '%s', supported pipeline runners %s",
runner, cache.getSupportedRunners());
throw new IllegalArgumentException(msg, e);
}
}
} else if (isCollectionOrArrayOfAllowedTypes(returnType, type)) {
// Split any strings with ","
List<String> values =
entry.getValue().stream()View on GitHub (pinned to 12126d8942)
Solutions
- Make the named class implement/extend PipelineRunner
- Set --runner to a known runner (DirectRunner, DataflowRunner, FlinkRunner, SparkRunner) or its registered short name
- Verify the fully-qualified class name corresponds to an actual runner class
Example fix
// before --runner=org.apache.beam.sdk.options.PipelineOptions // after --runner=org.apache.beam.runners.direct.DirectRunner
Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(runnerClass); if (!PipelineRunner.class.isAssignableFrom(c)) throw new IllegalArgumentException(runnerClass + " is not a PipelineRunner");
Try / catch
try { factory.fromArgs(args).create(); } catch (IllegalArgumentException e) { /* fall back to DirectRunner */ } Prevention
- Use registered short names (DirectRunner, DataflowRunner) instead of raw class names
- Verify the class hierarchy of custom runners extends PipelineRunner
When it happens
Trigger: Passing --runner=com.example.MyRunner (or a runner map entry) where the class exists on the classpath but is not a PipelineRunner subclass.
Common situations: Pointing --runner at a PipelineOptions class or pipeline class by mistake; copying a class name from an unrelated project; refactoring moved the runner out of the PipelineRunner hierarchy.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Unknown 'runner' specified '%s', supported pipeline runners
- Runner does not support draining.
- Scheme: [%s] has conflicting filesystems: [%s]
- Property [%s] is marked with contradictory annotations. Foun
- All inherited interfaces of [%s] should inherit from the Pip
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/948f76c37986bfa9.
Report an issue: GitHub.