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
- Point className/sourceClass at a class that implements org.apache.pulsar.io.core.Source (or BatchSource)
- If the class is a Sink, register it via pulsar-admin sinks instead of sources
- If it's your own connector, declare 'implements Source<T>' and implement open/recordRead etc.
- 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
- Only submit classes implementing org.apache.pulsar.io.core.Source or BatchSource to the sources API
- Register Sink implementations through the sinks API, not sources
- Declare 'implements Source<T>' (with open/read) when writing custom connectors
- For batch ingestion use BatchSource plus batchSourceConfig in the SourceConfig
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
- Window function must take a collection as input
- Function runtime customizer %s must implement KubernetesMani
- Sink transform function output must be of type Record
- %s does not implement %s
- Serializer type mismatch
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/7a110386c9c97b28.
Report an issue: GitHub.