apache/pulsar · critical · IOException

Class ${handlerClass} does not implement worker service inte

Error message

Class ${handlerClass} does not implement worker service interface

What it means

WorkerServiceLoader.load() instantiates the handler class declared in the NAR's WorkerServiceDefinition. If the instantiated object does not implement the org.apache.pulsar.functions.worker.service.WorkerService interface, loading fails with this IOException. The NAR metadata exists but points at a class with the wrong type.

Source

Thrown at pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/WorkerServiceLoader.java:93

                                             String narExtractionDirectory) throws IOException {
        final File narFile = metadata.getArchivePath().toAbsolutePath().normalize().toFile();
        NarClassLoader ncl = NarClassLoaderBuilder.builder()
                .narFile(narFile)
                .parentClassLoader(WorkerService.class.getClassLoader())
                .extractionDirectory(narExtractionDirectory)
                .build();

        WorkerServiceDefinition phDef = getWorkerServiceDefinition(ncl);
        if (StringUtils.isBlank(phDef.getHandlerClass())) {
            throw new IOException("Functions Worker Service Nar Package `" + phDef.getName()
                + "` does NOT provide a functions worker service implementation");
        }

        try {
            Class<?> handlerClass = ncl.loadClass(phDef.getHandlerClass());
            Object handler = handlerClass.getDeclaredConstructor().newInstance();
            if (!(handler instanceof WorkerService)) {
                throw new IOException("Class " + phDef.getHandlerClass()
                    + " does not implement worker service interface");
            }
            WorkerService ph = (WorkerService) handler;
            return new WorkerServiceWithClassLoader(ph, ncl);
        } catch (Throwable t) {
            rethrowIOException(t);
            return null;
        }
    }

    private static void rethrowIOException(Throwable cause)
            throws IOException {
        if (cause instanceof IOException) {
            throw (IOException) cause;
        } else if (cause instanceof RuntimeException) {
            throw (RuntimeException) cause;
        } else if (cause instanceof Error) {
            throw (Error) cause;

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the declared handler class implement org.apache.pulsar.functions.worker.service.WorkerService
  2. Fix the handlerClass entry in the NAR's WorkerServiceDefinition to point at the correct class
  3. Rebuild the NAR against the same Pulsar version as the broker so the WorkerService interface matches

Example fix

// before
public class MyWorker { /* no WorkerService */ }
// after
public class MyWorker implements WorkerService {
    @Override public void initialize(WorkerConfig config, AuthenticationProtocol auth) { ... }
    @Override public void start() { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// confirm the handler class in the NAR implements WorkerService before deployment
classpath-load handlerClass;
if (!WorkerService.class.isInstance(instantiated)) throw new IllegalStateException("handlerClass is not a WorkerService");

Prevention

When it happens

Trigger: A NAR whose WorkerServiceDefinition.handlerClass names a class that exists and instantiates but is not a WorkerService (custom fork drift, wrong class name in metadata, or a re-implemented handler after an interface rename).

Common situations: Building a custom worker NAR against an incompatible Pulsar API version where WorkerService changed; typos or copy-paste errors in the handlerClass metadata entry; refactoring a handler class without updating its implemented interface.

Related errors


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