apache/pulsar · critical · RuntimeException

User class must either be Function or java.util.Function

Error message

User class must either be Function or java.util.Function

What it means

During JavaInstanceRunnable.setup, the user's class is instantiated and validated: the object must implement org.apache.pulsar.functions.api.Function (or the older WindowFunction path) or java.util.function.Function. If neither interface is implemented, a RuntimeException is thrown because the instance has no callable function interface to invoke. This catches the common mistake of loading a class that isn't actually a function.

Source

Thrown at pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceRunnable.java:265

                .attr("function", instanceConfig.getFunctionDetails().getName())
                .attr("details", instanceConfig.getFunctionDetails())
                .log("Starting Java Instance");

        Object object;
        if (instanceConfig.getFunctionDetails().getClassName()
                .equals(org.apache.pulsar.functions.windowing.WindowFunctionExecutor.class.getName())) {
            object = Reflections.createInstance(
                    instanceConfig.getFunctionDetails().getClassName(),
                    instanceClassLoader);
        } else {
            object = Reflections.createInstance(
                    instanceConfig.getFunctionDetails().getClassName(),
                    functionClassLoader);
        }


        if (!(object instanceof Function) && !(object instanceof java.util.function.Function)) {
            throw new RuntimeException("User class must either be Function or java.util.Function");
        }

        // start the state table
        setupStateStore();

        ContextImpl contextImpl = setupContext();

        // start the output producer
        setupOutput(contextImpl);
        // start the input consumer
        setupInput(contextImpl);
        // start any log topic handler
        setupLogHandler();

        if (!(object instanceof IdentityFunction) && !(sink instanceof PulsarSink)) {
            sinkSchemaInfoProvider = new SinkSchemaInfoProvider();
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the user class implement org.apache.pulsar.functions.api.Function<I,O> (or java.util.function.Function) and redeploy.
  2. Verify the function's className setting points at the correct fully-qualified class in the uploaded jar.
  3. Check the function's jar/classloader contains the right version of the class (no stale jars).

Example fix

// before
public class MyProcessor {
    public String process(String input) { return input.toUpperCase(); }
}
// after
public class MyProcessor implements Function<String, String> {
    @Override
    public String process(String input, Context context) { return input.toUpperCase(); }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = functionClassLoader.loadClass(className);
if (!org.apache.pulsar.functions.api.Function.class.isAssignableFrom(c)
    && !java.util.function.Function.class.isAssignableFrom(c)) {
    throw new IllegalArgumentException(className + " must implement Function or java.util.function.Function");
}

Type guard

boolean isUserFunction(Object o) {
  return o instanceof org.apache.pulsar.functions.api.Function
      || o instanceof java.util.function.Function;
}

Try / catch

try { new JavaInstanceRunnable(...); } catch (RuntimeException e) { if (e.getMessage().startsWith("User class must")) { throw new IllegalStateException("Fix function className or implement Function", e); } throw e; }

Prevention

When it happens

Trigger: Deploying a function whose className points to a class that implements neither pulsar Function nor java.util.function.Function — e.g. a helper class, a class implementing only the deprecated deprecated Function variants, or a wrong class name resolving to a different type in the specified classloader.

Common situations: Typo/wrong value in the function's className config; jar containing multiple versions of the class; upgrading code where the function no longer implements the API; using Scala/Groovy lambdas or classes that implement similar but distinct interfaces.

Related errors


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