apache/pulsar · error · IOException

Class %s does not implement interface %s or %s

Error message

Class %s does not implement interface %s or %s

What it means

getIOSourceClass loads the declared sourceClass from the connector NAR and verifies it implements org.apache.pulsar.functions.api.Source or BatchSource. If the loaded class implements neither, an IOException is thrown because the class cannot be used as a Pulsar IO source.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/io/ConnectorUtils.java:79

    public static String computeArchiveChecksumHex(Path path) throws IOException {
        return HexFormat.of().formatHex(FileUtils.calculateSha256sum(path.toAbsolutePath().normalize().toFile()));
    }

    /**
     * Extract the Pulsar IO Source class from a connector archive.
     */
    public static String getIOSourceClass(NarClassLoader narClassLoader) throws IOException {
        ConnectorDefinition conf = getConnectorDefinition(narClassLoader);
        if (StringUtils.isEmpty(conf.getSourceClass())) {
            throw new IOException(
                    String.format("The '%s' connector does not provide a source implementation", conf.getName()));
        }

        try {
            // Try to load source class and check it implements Source interface
            Class<?> sourceClass = narClassLoader.loadClass(conf.getSourceClass());
            if (!(Source.class.isAssignableFrom(sourceClass) || BatchSource.class.isAssignableFrom(sourceClass))) {
                throw new IOException(String.format("Class %s does not implement interface %s or %s",
                        conf.getSourceClass(), Source.class.getName(), BatchSource.class.getName()));
            }
        } catch (Throwable t) {
            Exceptions.rethrowIOException(t);
        }

        return conf.getSourceClass();
    }

    /**
     * Extract the Pulsar IO Sink class from a connector archive.
     */
    public static String getIOSinkClass(NarClassLoader narClassLoader) throws IOException {
        ConnectorDefinition conf = getConnectorDefinition(narClassLoader);
        if (StringUtils.isEmpty(conf.getSinkClass())) {
            throw new IOException(
                    String.format("The '%s' connector does not provide a sink implementation", conf.getName()));
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Set sourceClass in the connector metadata to a class implementing org.apache.pulsar.functions.api.Source (or BatchSource)
  2. Rebuild the connector NAR after fixing the class
  3. Check the class implements the interface with the same API version the runtime uses

Example fix

// before
public class MyConnector { /* no interface */ }
// after
public class MyConnector implements Source { public void open(...) {...} public Record<String> read() {...} ... }
Defensive patterns

Strategy: validation

Validate before calling

String cls = ConnectorUtils.getConnectorDefinition(loader).getSourceClass();
Class<?> c = Class.forName(cls, false, loader);
if (!Source.class.isAssignableFrom(c) && !BatchSource.class.isAssignableFrom(c)) {
    throw new IllegalArgumentException(cls + " is not a Source");
}

Type guard

boolean isValidSourceClass(String name, NarClassLoader loader) throws ClassNotFoundException {
    Class<?> c = loader.loadClass(name);
    return Source.class.isAssignableFrom(c) || BatchSource.class.isAssignableFrom(c);
}

Try / catch

try {
    String src = ConnectorUtils.getIOSourceClass(loader);
} catch (IOException e) {
    if (e.getMessage().contains("does not implement interface")) {
        // report bad connector build to connector maintainer
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling getIOSourceClass(narClassLoader) where the connector definition names a class that loads successfully but does not implement Source or BatchSource.

Common situations: Connector author pointed sourceClass at a helper/raw class instead of the Source implementation; refactoring renamed or dropped the interface; wrong class name pasted into connector metadata.

Related errors


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