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

  1. Make the named class implement/extend PipelineRunner
  2. Set --runner to a known runner (DirectRunner, DataflowRunner, FlinkRunner, SparkRunner) or its registered short name
  3. 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

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


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/948f76c37986bfa9. Report an issue: GitHub.