apache/pulsar · error · IllegalArgumentException

%s does not implement %s

Error message

%s does not implement %s

What it means

Thrown by ClassLoaderUtils.implementsClass when the class loads successfully but is not assignable to the required klass parameter — i.e. it does not implement/extend the expected interface or superclass. This guards plugin and strategy loading so only type-correct implementations are accepted.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/ClassLoaderUtils.java:76

            if (classLoader != null) {
                objectClass = classLoader.loadClass(className);
            } else {
                throw e;
            }
        }
        return objectClass;
    }

    public static void implementsClass(String className, Class<?> klass, ClassLoader classLoader) {
        Class<?> objectClass;
        try {
            objectClass = loadClass(className, classLoader);
        } catch (ClassNotFoundException | NoClassDefFoundError e) {
            throw new IllegalArgumentException("Cannot find/load class " + className);
        }

        if (!klass.isAssignableFrom(objectClass)) {
            throw new IllegalArgumentException(
                    String.format("%s does not implement %s", className, klass.getName()));
        }
    }

    public static void closeClassLoader(ClassLoader classLoader) {
        if (classLoader instanceof Closeable) {
            try {
                ((Closeable) classLoader).close();
            } catch (IOException e) {
                log.error().attr("classLoader", classLoader).exception(e).log("Error closing classloader");
            }
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Confirm the configured class actually implements/extends the expected interface (check its class hierarchy)
  2. Point the configuration at the correct implementation class, not a helper or wrapper
  3. Rebuild the plugin against the same Pulsar API version as the broker so interface signatures match
  4. Use the full cause chain: load the class yourself and print it, checking klass.isAssignableFrom beforehand

Example fix

// before
ClassLoaderUtils.implementsClass("com.acme.MyHandler", Function.class, cl); // MyHandler implements Runnable
// after
public class MyHandler implements Function<byte[], byte[]> { ... }
ClassLoaderUtils.implementsClass("com.acme.MyHandler", Function.class, cl);
Defensive patterns

Strategy: type-guard

Validate before calling

Class<?> c = Class.forName(className, false, classLoader);
if (!klass.isAssignableFrom(c)) {
    throw new IllegalArgumentException(className + " does not implement " + klass.getName());
}

Type guard

static <T> Class<? extends T> asImplementing(String className, Class<T> klass, ClassLoader cl) throws ClassNotFoundException {
    Class<?> c = Class.forName(className, false, cl);
    if (!klass.isAssignableFrom(c)) return null;
    return c.asSubclass(klass);
}

Try / catch

try {
    ClassLoaderUtils.implementsClass(className, klass, cl);
} catch (IllegalArgumentException e) {
    log.error("Class {} exists but does not implement {}", className, klass.getName());
}

Prevention

When it happens

Trigger: Calling implementsClass(className, expectedType, classLoader) where the loaded class compiles and loads but does not extend/implement expectedType — e.g. registering a class as a PulsarFunction when it doesn't implement the Function interface.

Common situations: Pointing configuration at the wrong class inside a jar (the impl class vs a helper class); loading a plugin built against a different Pulsar interface version; refactoring that moved an interface so the class silently stops implementing it; confusing similar interfaces with the same simple name.

Related errors


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