apache/pulsar · error · IOException

Class ${sinkClass} does not implement interface org.apache.p

Error message

Class ${sinkClass} does not implement interface org.apache.pulsar.functions.api.Sink

What it means

getIOSinkClass loads the declared sinkClass from the connector NAR and verifies it implements org.apache.pulsar.functions.api.Sink. If it does not, an IOException is thrown since the class is not a valid Pulsar IO sink.

Source

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

        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()));
        }

        try {
            // Try to load sink class and check it implements Sink interface
            Class<?> sinkClass = narClassLoader.loadClass(conf.getSinkClass());
            if (!(Sink.class.isAssignableFrom(sinkClass))) {
                throw new IOException(
                        "Class " + conf.getSinkClass() + " does not implement interface " + Sink.class.getName());
            }
        } catch (Throwable t) {
            Exceptions.rethrowIOException(t);
        }

        return conf.getSinkClass();
    }

    public static ConnectorDefinition getConnectorDefinition(File narFile) throws IOException {
        return FunctionUtils.getPulsarIOServiceConfig(narFile, ConnectorDefinition.class);
    }

    public static ConnectorDefinition getConnectorDefinition(NarClassLoader narClassLoader) throws IOException {
        return FunctionUtils.getPulsarIOServiceConfig(narClassLoader, ConnectorDefinition.class);
    }

    public static List<ConfigFieldDefinition> getConnectorConfigDefinition(

View on GitHub (pinned to 820761864e)

Solutions

  1. Point sinkClass at a class implementing org.apache.pulsar.functions.api.Sink and rebuild the NAR
  2. Ensure the correct (not duplicated) class name is in the connector metadata
  3. Verify interface compatibility between the connector's pulsar-io-core version and the runtime

Example fix

// before
sinkClass=com.example.MyHelper  // not a Sink
// after
sinkClass=com.example.MySink  // implements org.apache.pulsar.functions.api.Sink
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

boolean isValidSinkClass(String name, NarClassLoader loader) throws ClassNotFoundException {
    return Sink.class.isAssignableFrom(loader.loadClass(name));
}

Try / catch

try {
    String sink = ConnectorUtils.getIOSinkClass(loader);
} catch (IOException e) {
    if (e.getMessage().contains("does not implement interface")) {
        // fail fast: connector metadata points at a non-Sink class
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling getIOSinkClass(narClassLoader) where the metadata's sinkClass resolves to a class that does not implement Sink.

Common situations: sinkClass pointing at a Source or plain class; copy-paste of sourceClass into sinkClass; interface dropped during a connector upgrade/refactor.

Related errors


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