apache/pulsar · error · RuntimeException
does not implement
Error message
does not implement
What it means
After loading the class, createInstance checks `xface.isAssignableFrom(theCls)`. If the loaded class does not implement/extend the requested interface, this RuntimeException is thrown with a message of the form `<className> does not implement <interfaceName>`. It guards against misconfigured class names pointing at unrelated classes.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/Reflections.java:75
* Create an instance of <code>userClassName</code> using provided <code>classLoader</code>.
* This instance should implement the provided interface <code>xface</code>.
*
* @param userClassName user class name
* @param xface the interface that the reflected instance should implement
* @param classLoader class loader to load the class.
* @return the instance
*/
public static <T> T createInstance(String userClassName,
Class<T> xface,
ClassLoader classLoader) {
Class<?> theCls;
try {
theCls = Class.forName(userClassName, true, classLoader);
} catch (ClassNotFoundException | NoClassDefFoundError cnfe) {
throw new RuntimeException("User class must be in class path", cnfe);
}
if (!xface.isAssignableFrom(theCls)) {
throw new RuntimeException(userClassName + " does not implement " + xface.getName());
}
@SuppressWarnings("unchecked") // safe: theCls is verified to be assignable to xface
Class<T> tCls = (Class<T>) theCls.asSubclass(xface);
T result;
try {
@SuppressWarnings("unchecked") // safe: constructor cache is keyed by theCls which extends T
Constructor<T> meth = (Constructor<T>) constructorCache.get(theCls);
if (null == meth) {
meth = tCls.getDeclaredConstructor();
meth.setAccessible(true);
constructorCache.put(theCls, meth);
}
result = meth.newInstance();
} catch (InstantiationException ie) {
throw new RuntimeException("User class must be concrete", ie);
} catch (NoSuchMethodException e) {
throw new RuntimeException("User class must have a no-arg constructor", e);
} catch (IllegalAccessException e) {View on GitHub (pinned to 820761864e)
Solutions
- Make the class implement/extend the interface named in the message
- Fix the configured class name to one that implements the expected interface
- After upgrades, recompile the plugin against the new interface package/signature
Example fix
// before
class MyFilter { } // does not implement Filter
// after
class MyFilter implements Filter { } Defensive patterns
Strategy: validation
Validate before calling
// verify the interface relationship before instantiating
Class<?> cls = Class.forName(className, true, classLoader);
if (!XFace.class.isAssignableFrom(cls)) {
throw new IllegalArgumentException(className + " does not implement " + XFace.getName());
} Type guard
boolean implementsX(Class<?> cls) { return XFace.class.isAssignableFrom(cls); } Try / catch
try {
T obj = Reflections.createInstance(className, XFace.class, classLoader);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains(" does not implement ")) {
throw new IllegalArgumentException("Wrong class configured: " + e.getMessage(), e);
}
throw e;
} Prevention
- Copy fully qualified class names from the plugin's own documentation/source, not from memory
- After upgrades, recompile plugins against the current interface packages
- Add a config-validation step at startup that checks isAssignableFrom before use
When it happens
Trigger: Configuring a class name that exists on the classpath but does not implement the expected interface `xface`, e.g. pointing an interceptor setting at a random class.
Common situations: Copy-pasting a class name from another config key; upgrading Pulsar where the interface moved packages so the old class no longer matches; implementing the wrong interface version.
Related errors
- is not an instance of MetadataStore
- Failed to instantiate ${className}
- Exception caused while converting configuration: ${message}
- Failed to compute configuration overrides
- Failed to load config into existing configuration data
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/a91770a0764a4197.
Report an issue: GitHub.