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
- Confirm the configured class actually implements/extends the expected interface (check its class hierarchy)
- Point the configuration at the correct implementation class, not a helper or wrapper
- Rebuild the plugin against the same Pulsar API version as the broker so interface signatures match
- 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
- Check the class hierarchy of plugin classes before registering them
- Point config at the concrete implementation, not helper classes
- Rebuild plugins against the broker's API version
- Keep interfaces stable; bump plugin versions when interfaces change
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
- Cannot find/load class
- Error when loading topic compaction strategy:
- ClassNotFoundException: className
- Failed to load an authorization provider.
- Failed to instantiate ${className}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/8cc7143645bba997.
Report an issue: GitHub.