apache/pulsar · error · IOException

Class ${extensionClass} does not implement extension interfa

Error message

Class ${extensionClass} does not implement extension interface

What it means

Thrown when the class named in the NAR's extensionClass descriptor is loaded and instantiated but is not an instance of ProxyExtension. The descriptor exists, yet points at the wrong class — often an implementation of a different interface or a support class.

Source

Thrown at pulsar-proxy/src/main/java/org/apache/pulsar/proxy/extensions/ProxyExtensionsUtils.java:142

                                              String narExtractionDirectory) throws IOException {
        final File narFile = metadata.getArchivePath().toAbsolutePath().normalize().toFile();
        NarClassLoader ncl = NarClassLoaderBuilder.builder()
                .narFile(narFile)
                .parentClassLoader(ProxyExtension.class.getClassLoader())
                .extractionDirectory(narExtractionDirectory)
                .build();

        ProxyExtensionDefinition phDef = getProxyExtensionDefinition(ncl);
        if (StringUtils.isBlank(phDef.getExtensionClass())) {
            throw new IOException("extension `" + phDef.getName() + "` does NOT provide a protocol"
                + " handler implementation");
        }

        try {
            Class<?> extensionClass = ncl.loadClass(phDef.getExtensionClass());
            Object extension = extensionClass.getDeclaredConstructor().newInstance();
            if (!(extension instanceof ProxyExtension)) {
                throw new IOException("Class " + phDef.getExtensionClass()
                    + " does not implement extension interface");
            }
            ProxyExtension ph = (ProxyExtension) extension;
            return new ProxyExtensionWithClassLoader(ph, ncl);
        } catch (Throwable t) {
            rethrowIOException(t);
            return null;
        }
    }

    private static void rethrowIOException(Throwable cause)
            throws IOException {
        if (cause instanceof IOException) {
            throw (IOException) cause;
        } else if (cause instanceof RuntimeException) {
            throw (RuntimeException) cause;
        } else if (cause instanceof Error) {
            throw (Error) cause;

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the configured class implement org.apache.pulsar.proxy.extensions.ProxyExtension (or change extensionClass to one that does)
  2. Rebuild the NAR against the same Pulsar version as the running proxy so the interface matches
  3. Verify the class with 'javap -classpath ... <class>' to confirm it implements ProxyExtension
  4. Check the extension's changelog for interface migrations when upgrading Pulsar

Example fix

// before
public class MyExtension { public void init(...) {} }
// after
public class MyExtension implements ProxyExtension {
  @Override public boolean accept(String extension) { return "myext".equals(extension); }
  // ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time check in the extension project
Class<?> c = Class.forName("org.example.MyProxyExtension");
if (!ProxyExtension.class.isAssignableFrom(c))
    throw new IllegalStateException("extensionClass must implement ProxyExtension");

Type guard

// Java instanceof narrowing
Object o = extensionClass.getDeclaredConstructor().newInstance();
if (o instanceof ProxyExtension) {
    ProxyExtension ext = (ProxyExtension) o; // safe
}

Try / catch

try { proxyExtensions.load(conf); } catch (RuntimeException e) { if (e.getMessage().contains("does not implement extension interface")) log.error("extensionClass points at a non-ProxyExtension class"); throw e; }

Prevention

When it happens

Trigger: ProxyExtensionsUtils.load loading a NAR whose extensionClass names a class that does not implement org.apache.pulsar.proxy.extensions.ProxyExtension (or was compiled against a Pulsar version where the interface changed/moved).

Common situations: Copy-pasting an old example class that implements a deprecated API; building the extension against mismatched Pulsar client/proxy dependency versions; pointing extensionClass at a factory or config class by mistake.

Related errors


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