apache/pulsar · error · IllegalArgumentException

Source class %s does not implement the correct interface

Error message

Source class %s does not implement the correct interface

What it means

validateAndExtractDetails verifies that the resolved source class implements org.apache.pulsar.io.core.Source or BatchSource via ByteBuddy erasure assignability checks. If it implements neither, the class loads fine but is not a usable source, so this IllegalArgumentException is thrown with the class name.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SourceConfigUtils.java:316

            }
            sourceClassName = connectorDefinition.getSourceClass();
            if (sourceClassName == null) {
                throw new IllegalArgumentException("Failed to extract source class from archive");
            }
        }

        // check if source implements the correct interfaces
        TypeDescription sourceClass;
        try {
            sourceClass = sourceFunction.resolveType(sourceClassName);
        } catch (TypePool.Resolution.NoSuchTypeException e) {
            throw new IllegalArgumentException(
              String.format("Source class %s not found in class loader", sourceClassName), e);
        }

        if (!(sourceClass.asErasure().isAssignableTo(Source.class) || sourceClass.asErasure()
                .isAssignableTo(BatchSource.class))) {
            throw new IllegalArgumentException(
                    String.format("Source class %s does not implement the correct interface",
                            sourceClass.getName()));
        }

        if (sourceClass.asErasure().isAssignableTo(BatchSource.class)) {
            if (sourceConfig.getBatchSourceConfig() != null) {
                validateBatchSourceConfig(sourceConfig.getBatchSourceConfig());
            } else {
                throw new IllegalArgumentException(
                  String.format("Source class %s implements %s but batch source source config is not specified",
                    sourceClass.getName(), BatchSource.class.getName()));
            }
        }

        // extract type from source class
        TypeDefinition typeArg;

        try {

View on GitHub (pinned to 820761864e)

Solutions

  1. Point className/sourceClass at a class that implements org.apache.pulsar.io.core.Source (or BatchSource)
  2. If the class is a Sink, register it via pulsar-admin sinks instead of sources
  3. If it's your own connector, declare 'implements Source<T>' and implement open/recordRead etc.
  4. Confirm with 'implements BatchSource<T>' plus a BatchSourceConfig when doing batch ingestion, and set batchSourceConfig accordingly

Example fix

// before
public class MyConnector implements Sink<String> { ... } // registered as source
// after
public class MyConnector implements Source<String> {
    public void open(Map<String, Object> config, SourceContext ctx) { ... }
    public Record<String> read() throws Exception { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the class implements the right interface before submit:
Class<?> c = Class.forName(cfg.getClassName(), false, getClass().getClassLoader());
if (!org.apache.pulsar.io.core.Source.class.isAssignableFrom(c)
        && !org.apache.pulsar.io.core.BatchSource.class.isAssignableFrom(c)) {
    throw new IllegalStateException(cfg.getClassName() + " does not implement Source/BatchSource");
}

Type guard

static boolean isSourceClass(Class<?> c) {
    return org.apache.pulsar.io.core.Source.class.isAssignableFrom(c)
        || org.apache.pulsar.io.core.BatchSource.class.isAssignableFrom(c);
}

Try / catch

try {
    SourceConfigUtils.validateAndExtractDetails(cfg, pkg, true);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("does not implement the correct interface")) {
        // wrong connector type: use the sinks API or pick a class implementing Source
    }
}

Prevention

When it happens

Trigger: Setting className (or a pulsar-io.yaml sourceClass) to a class that doesn't implement Source/BatchSource — e.g. pointing at a Sink implementation, a helper class, or a custom connector class missing the 'implements Source' declaration; also BatchSource implementations that require BatchSourceConfig handling.

Common situations: Registering a sink connector through the sources API; copying a className from a sink config into a source config; writing a connector class that implements the wrong interface or an old internal one; generically validating a package against the wrong function type.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/7a110386c9c97b28. Report an issue: GitHub.