apache/beam · error · IllegalArgumentException

CDAP plugin class must have Plugin annotation!

Error message

CDAP plugin class must have Plugin annotation!

What it means

Plugin.isUnbounded determines whether a streaming source is unbounded by reading the io.cdap.cdap.api.annotation.Plugin annotation on the plugin class and checking if its type starts with "streaming". If no CDAP Plugin annotation is present, isUnbounded remains null and the method throws IllegalArgumentException, since boundedness cannot be determined without the annotation-declared plugin type.

Solutions

  1. Annotate the plugin class with io.cdap.cdap.api.annotation.Plugin including a correct type value (e.g., "streamingsource", "batchsource", "batchsink")
  2. Verify the plugin jar retains the annotation at RUNTIME retention after your build/shading
  3. Pass the actual annotated plugin class, not a wrapper or config class
  4. Check imports: annotate with the CDAP Plugin annotation, not another annotation with the same simple name

Example fix

// before
public class MyStreamSource { ... }
// after
@Plugin(type = PluginConstants.PluginType.STREAMING_SOURCE)
public class MyStreamSource { ... }
Defensive patterns

Strategy: validation

Validate before calling

boolean hasCdapPluginAnnotation(Class<?> c) {
  return java.util.Arrays.stream(c.getAnnotations())
      .anyMatch(a -> a.annotationType().equals(io.cdap.cdap.api.annotation.Plugin.class));
}

Type guard

static boolean hasCdapPluginAnnotation(Class<?> c) {
  return c.isAnnotationPresent(io.cdap.cdap.api.annotation.Plugin.class);
}

Try / catch

try {
  expand(...);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("Plugin annotation")) { /* fix the plugin class annotation */ }
  throw e;
}

Prevention

When it happens

Trigger: Calling expand/prepareRun/getReceiverBuilder with a CDAP plugin class that lacks @Plugin(type = ...) — e.g., a hand-written class, a test stub, or a plugin whose annotation was lost after build/shading.

Common situations: Writing a custom CDAP plugin and forgetting the @Plugin annotation; annotation retention stripped during build; passing a plain config or wrapper class where the actual annotated plugin class was expected; CDAP SDK upgrade changing annotation package so the checked annotation type no longer matches.

Related errors


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

Appendix: source

Thrown at sdks/java/io/cdap/src/main/java/org/apache/beam/sdk/io/cdap/Plugin.java:245

      } else if (method.getName().equals(GET_STREAM_METHOD_NAME)) {
        return new StreamingSourceContextImpl();
      }
    }
    throw new IllegalStateException("Cannot determine context class");
  }

  /** Gets value of a plugin type. */
  public Boolean isUnbounded() {
    Boolean isUnbounded = null;

    for (Annotation annotation : getPluginClass().getDeclaredAnnotations()) {
      if (annotation.annotationType().equals(io.cdap.cdap.api.annotation.Plugin.class)) {
        String pluginType = ((io.cdap.cdap.api.annotation.Plugin) annotation).type();
        isUnbounded = pluginType != null && pluginType.startsWith("streaming");
      }
    }
    if (isUnbounded == null) {
      throw new IllegalArgumentException("CDAP plugin class must have Plugin annotation!");
    }
    return isUnbounded;
  }

  /** Gets a {@link ReceiverBuilder}. */
  public ReceiverBuilder<V, ? extends Receiver<V>> getReceiverBuilder() {
    checkState(isUnbounded(), "Receiver Builder is supported only for unbounded plugins");

    Class<?> pluginClass = getPluginClass();
    Class<? extends Receiver<V>> receiverClass = getReceiverClass();
    SerializableFunction<PluginConfig, Object[]> getReceiverArgsFromConfigFn =
        getGetReceiverArgsFromConfigFn();
    PluginConfig pluginConfig = getPluginConfig();

    checkStateNotNull(pluginConfig, "Plugin config can not be null!");
    checkStateNotNull(pluginClass, "Plugin class can not be null!");
    checkStateNotNull(receiverClass, "Receiver class can not be null!");
    checkStateNotNull(

View on GitHub (pinned to 12126d8942)