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

  1. Make the class implement/extend the interface named in the message
  2. Fix the configured class name to one that implements the expected interface
  3. 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

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


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