apache/pulsar · error · IllegalArgumentException

Function class %s does not implement the correct interface

Error message

Function class %s does not implement the correct interface

What it means

doJavaChecks resolves the configured class in the function package and verifies it implements one of the supported interfaces: org.apache.pulsar.functions.api.Function, java.util.function.Function, or WindowFunction. If the resolved type implements none of them, IllegalArgumentException is thrown.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:650

            // thus we should try to find its class name in the NAR service definition
            if (functionClassName == null) {
                FunctionDefinition functionDefinition =
                        validatableFunctionPackage.getFunctionMetaData(FunctionDefinition.class);
                if (functionDefinition == null) {
                    throw new IllegalArgumentException("Function class name is not provided.");
                }
                functionClassName = functionDefinition.getFunctionClass();
                if (functionClassName == null) {
                    throw new IllegalArgumentException("Function class name is not provided.");
                }
            }
            functionClass = validatableFunctionPackage.resolveType(functionClassName);

            if (!functionClass.asErasure().isAssignableTo(org.apache.pulsar.functions.api.Function.class)
                    && !functionClass.asErasure().isAssignableTo(java.util.function.Function.class)
                    && !functionClass.asErasure()
                    .isAssignableTo(org.apache.pulsar.functions.api.WindowFunction.class)) {
                throw new IllegalArgumentException(
                        String.format("Function class %s does not implement the correct interface",
                                functionClassName));
            }
        } catch (TypePool.Resolution.NoSuchTypeException e) {
            throw new IllegalArgumentException(
                    String.format("Function class %s must be in class path", functionClassName), e);
        }

        TypeDefinition[] typeArgs = FunctionCommon.getFunctionTypes(functionConfig, functionClass);
        // inputs use default schema, so there is no check needed there

        // Check if the Input serialization/deserialization class exists in jar or already loaded and that it
        // implements SerDe class
        if (functionConfig.getCustomSerdeInputs() != null) {
            functionConfig.getCustomSerdeInputs().forEach((topicName, inputSerializer) -> {
                ValidatorUtils.validateSerde(inputSerializer, typeArgs[0], validatableFunctionPackage.getTypePool(),
                        true);
            });

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the configured class implement org.apache.pulsar.functions.api.Function (or WindowFunction / java.util.function.Function)
  2. Correct the className so it points to the actual function class
  3. Ensure the interface is from the same Pulsar client API version as the validator

Example fix

// before
public class MyProcessor { public String process(String in) { return in; } }
// after
public class MyProcessor implements org.apache.pulsar.functions.api.Function<String, String> {
    public String apply(String in) { return in; }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = Class.forName(config.getClassName(), false, loader); if (!Function.class.isAssignableFrom(c) && !java.util.function.Function.class.isAssignableFrom(c) && !WindowFunction.class.isAssignableFrom(c)) { throw new IllegalArgumentException(config.getClassName() + " must implement a supported function interface"); }

Type guard

boolean isSupportedFunction(Class<?> c) { return Function.class.isAssignableFrom(c) || java.util.function.Function.class.isAssignableFrom(c) || WindowFunction.class.isAssignableFrom(c); }

Try / catch

try { ... } catch (IllegalArgumentException e) { if (e.getMessage().contains("correct interface")) { log.error("Fix implements clause on {}", config.getClassName()); } }

Prevention

When it happens

Trigger: FunctionConfig.className points to a class that does not implement any supported function interface, e.g. a plain class or a class implementing only a different framework's interface.

Common situations: Typo in the class name resolving to the wrong type; refactoring removed the implements clause; pointing the config at a helper class instead of the actual function class.

Related errors


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