apache/pulsar · error · RuntimeException

User class must be concrete

Error message

User class must be concrete

What it means

createInstance() instantiates the user class via its no-arg constructor. InstantiationException is thrown by the JVM when the loaded class is abstract or an interface; Pulsar rethrows it as RuntimeException("User class must be concrete") so the operator knows the submitted class cannot be instantiated.

Source

Thrown at pulsar-functions/runtime-all/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceMain.java:146

        }
    }

    public static Object createInstance(String userClassName,
                                        ClassLoader classLoader) {
        Class<?> theCls;
        try {
            theCls = Class.forName(userClassName, true, classLoader);
        } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {
            throw new RuntimeException("Class " + userClassName + " must be in class path", cnfe);
        }
        Object result;
        try {
            Constructor<?> meth = theCls.getDeclaredConstructor();
            meth.setAccessible(true);

            result = meth.newInstance();
        } catch (InstantiationException ie) {
            throw new RuntimeException("User class must be concrete", ie);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Class " + userClassName + " doesn't have such method", e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Class " + userClassName + " must have a no-arg constructor", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("Class " + userClassName + " constructor throws exception", e);
        }
        return result;
    }

    public static ClassLoader loadJar(ClassLoader parent, File[] jars) throws MalformedURLException {
        URL[] urls = new URL[jars.length];
        for (int i = 0; i < jars.length; i++) {
            urls[i] = jars[i].toURI().toURL();
        }
        return new URLClassLoader(urls, parent);
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Set className to a concrete public class with a no-arg constructor, not an abstract class or interface.
  2. Make the submitted function class non-abstract and implement Function<T,R> (or the relevant interface).
  3. Test instantiation locally with `new MyFunction()` or a tiny reflection snippet before deploying.

Example fix

// before
public abstract class MyFunction implements Function<String, Integer> { ... }
// after
public class MyFunction implements Function<String, Integer> { ... }
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = Class.forName(className);
if (java.lang.reflect.Modifier.isAbstract(c.getModifiers()) || c.isInterface()) {
  throw new IllegalArgumentException(className + " is abstract/interface; use a concrete class");
}

Type guard

boolean isConcreteInstantiable(String fcn) { try { Class<?> c = Class.forName(fcn); return !c.isInterface() && !java.lang.reflect.Modifier.isAbstract(c.getModifiers()); } catch (Throwable t) { return false; } }

Try / catch

try { Object f = JavaInstanceMain.createInstance(className, cl); } catch (RuntimeException e) { if ("User class must be concrete".equals(e.getMessage())) { System.err.println("Make " + className + " a concrete, non-abstract class"); } throw e; }

Prevention

When it happens

Trigger: Configuring a function whose className points to an abstract class, interface, or (in some JVM versions) an abstract base class instead of the concrete Function/WindowFunction implementation.

Common situations: Pointing className at a shared abstract base class; refactoring made the function abstract; accidentally submitting an interface name; anonymous/abstract adapter classes.

Related errors


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