apache/pulsar · error · RuntimeException
User class must be in class path
Error message
User class must be in class path
What it means
Reflections.createInstance(String, Class<T>, ClassLoader) loads a user-supplied class name and verifies it implements the requested interface. If Class.forName cannot load the class (ClassNotFoundException or NoClassDefFoundError), it wraps the failure in this RuntimeException. It means the configured class name is wrong or its dependencies are missing from the given classloader.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/Reflections.java:72
}
/**
* 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);View on GitHub (pinned to 820761864e)
Solutions
- Check the class name string for typos and correct package (fully qualified name)
- Confirm the jar/plugin bundle is actually available to the given classLoader
- Inspect the wrapped `cnfe` cause — NoClassDefFoundError usually names a missing dependency class
- Rebuild/redeploy the plugin after upgrades so the packaged class matches the new API
Example fix
// before
Reflections.createInstance("org.example.MyFilter", Filter.class, loader);
// after (corrected FQN and jar deployed)
Reflections.createInstance("org.example.plugins.MyEntryFilter", Filter.class, loader); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check that the class loads before calling createInstance
try {
Class.forName(className, true, classLoader);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
throw new IllegalArgumentException("Class not on classpath: " + className, e);
} Try / catch
try {
T obj = Reflections.createInstance(className, XFace.class, classLoader);
} catch (RuntimeException e) {
if (e.getMessage().equals("User class must be in class path")) {
throw new IllegalArgumentException("Check configured class name and plugin jar: " + className, e);
}
throw e;
} Prevention
- Store fully qualified class names in config, verified by a startup self-check
- Verify the plugin/Nar jar is deployed and loadable by the exact classloader you pass
- Re-check class names after any dependency or Pulsar version upgrade
When it happens
Trigger: Passing a class name string that is misspelled, uses the wrong package, or whose class file is absent from `classLoader`; the class exists but a referenced type in its signature is missing (NoClassDefFoundError).
Common situations: Typo in configuration like `interceptorName` or `entryFilterClassName`; fat-jar/plugin directory not on the NarClassLoader; class moved/renamed after a Pulsar upgrade.
Related errors
- (wraps ClassNotFoundException for BrokerEntryMetadataInterce
- (wraps ClassNotFoundException for interceptor)
- Class must be in class path
- 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/12c43a6a12f35369.
Report an issue: GitHub.