{"record":{"id":"3cdb369ab09e82c4","repo":"apache/pulsar","slug":"class-s-does-not-implement-interface-s-or-s","errorCode":null,"errorMessage":"Class %s does not implement interface %s or %s","messagePattern":"Class (.+?) does not implement interface (.+?) or (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/io/ConnectorUtils.java","lineNumber":79,"sourceCode":"    public static String computeArchiveChecksumHex(Path path) throws IOException {\n        return HexFormat.of().formatHex(FileUtils.calculateSha256sum(path.toAbsolutePath().normalize().toFile()));\n    }\n\n    /**\n     * Extract the Pulsar IO Source class from a connector archive.\n     */\n    public static String getIOSourceClass(NarClassLoader narClassLoader) throws IOException {\n        ConnectorDefinition conf = getConnectorDefinition(narClassLoader);\n        if (StringUtils.isEmpty(conf.getSourceClass())) {\n            throw new IOException(\n                    String.format(\"The '%s' connector does not provide a source implementation\", conf.getName()));\n        }\n\n        try {\n            // Try to load source class and check it implements Source interface\n            Class<?> sourceClass = narClassLoader.loadClass(conf.getSourceClass());\n            if (!(Source.class.isAssignableFrom(sourceClass) || BatchSource.class.isAssignableFrom(sourceClass))) {\n                throw new IOException(String.format(\"Class %s does not implement interface %s or %s\",\n                        conf.getSourceClass(), Source.class.getName(), BatchSource.class.getName()));\n            }\n        } catch (Throwable t) {\n            Exceptions.rethrowIOException(t);\n        }\n\n        return conf.getSourceClass();\n    }\n\n    /**\n     * Extract the Pulsar IO Sink class from a connector archive.\n     */\n    public static String getIOSinkClass(NarClassLoader narClassLoader) throws IOException {\n        ConnectorDefinition conf = getConnectorDefinition(narClassLoader);\n        if (StringUtils.isEmpty(conf.getSinkClass())) {\n            throw new IOException(\n                    String.format(\"The '%s' connector does not provide a sink implementation\", conf.getName()));\n        }","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/io/ConnectorUtils.java#L61-L97","documentation":"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.","triggerScenarios":"Calling getIOSourceClass(narClassLoader) where the connector definition names a class that loads successfully but does not implement Source or BatchSource.","commonSituations":"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.","solutions":["Set sourceClass in the connector metadata to a class implementing org.apache.pulsar.functions.api.Source (or BatchSource)","Rebuild the connector NAR after fixing the class","Check the class implements the interface with the same API version the runtime uses"],"exampleFix":"// before\npublic class MyConnector { /* no interface */ }\n// after\npublic class MyConnector implements Source { public void open(...) {...} public Record<String> read() {...} ... }","handlingStrategy":"validation","validationCode":"String cls = ConnectorUtils.getConnectorDefinition(loader).getSourceClass();\nClass<?> c = Class.forName(cls, false, loader);\nif (!Source.class.isAssignableFrom(c) && !BatchSource.class.isAssignableFrom(c)) {\n    throw new IllegalArgumentException(cls + \" is not a Source\");\n}","typeGuard":"boolean isValidSourceClass(String name, NarClassLoader loader) throws ClassNotFoundException {\n    Class<?> c = loader.loadClass(name);\n    return Source.class.isAssignableFrom(c) || BatchSource.class.isAssignableFrom(c);\n}","tryCatchPattern":"try {\n    String src = ConnectorUtils.getIOSourceClass(loader);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"does not implement interface\")) {\n        // report bad connector build to connector maintainer\n    } else { throw e; }\n}","preventionTips":["Always have connector classes implement the api Source/BatchSource interface","Run a smoke test that loads the NAR via ConnectorUtils in CI","Pin the pulsar-io-core dependency version to the target runtime"],"tags":["pulsar-io","connector","type-mismatch","source"],"backgroundTag":"class-does-not-implement-interface","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}