apache/pulsar · error · RuntimeException

Timestamp extractor class %s must be in class path

Error message

Timestamp extractor class %s must be in class path

What it means

When a custom timestampExtractorClassName is configured, getTimeStampExtractor loads it with Class.forName using the thread context classloader. If the class (or something it references) cannot be found or linked, the executor wraps the ClassNotFoundException/NoClassDefFoundError in a RuntimeException naming the class. This means the user's timestamp extractor jar is not shipped with the function or the class name is wrong.

Source

Thrown at pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/windowing/WindowFunctionExecutor.java:137

        EvictionPolicy<Record<T>, ?> evictionPolicy = getEvictionPolicy(windowConfig);
        TriggerPolicy<Record<T>, ?> triggerPolicy = getTriggerPolicy(windowConfig, manager,
                evictionPolicy, context);
        manager.setEvictionPolicy(evictionPolicy);
        manager.setTriggerPolicy(triggerPolicy);

        return manager;
    }

    @SuppressWarnings("unchecked")
    private TimestampExtractor<T> getTimeStampExtractor(WindowConfig windowConfig) {

        Class<?> theCls;
        try {
            theCls = Class.forName(windowConfig.getTimestampExtractorClassName(),
                    true, Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {
            throw new RuntimeException(
                    String.format("Timestamp extractor class %s must be in class path",
                            windowConfig.getTimestampExtractorClassName()), cnfe);
        }

        Object result;
        try {
            Constructor<?> constructor = theCls.getDeclaredConstructor();
            constructor.setAccessible(true);
            result = constructor.newInstance();
        } catch (InstantiationException ie) {
            throw new RuntimeException("User class must be concrete", ie);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("User class doesn't have such method", e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("User class must have a no-arg constructor", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("User class constructor throws exception", e);
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Bundle the timestamp extractor class (and its dependencies) in the function jar and redeploy
  2. Correct the fully-qualified class name in WindowConfig.timestampExtractorClassName
  3. Check function instance logs for the caused-by ClassNotFoundException to see the missing class name
  4. Ensure the class is on the function instance classpath, not only the broker's

Example fix

// before
"timestampExtractorClassName": "com.example.TimeStampExtractor"  // typo, class not uploaded
// after
"timestampExtractorClassName": "com.example.MyTimestampExtractor"  // included in function uber-jar
Defensive patterns

Strategy: validation

Validate before calling

try {
    Class.forName(className, true, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
    throw new IllegalStateException("extractor class not in function jar: " + className);
}

Try / catch

try {
    executor.initialize();
} catch (RuntimeException e) {
    if (e.getCause() instanceof ClassNotFoundException || e.getCause() instanceof NoClassDefFoundError) {
        throw new IllegalStateException("bundle the extractor class and its dependencies in the function jar", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: WindowConfig.timestampExtractorClassName set to a class not in the function's fat jar / nar; typo in fully-qualified name; NoClassDefFoundError from a missing transitive dependency of the extractor.

Common situations: Deploying a function without uploading the extractor class's jar; package rename after refactor; class present in broker but not in function instance classpath.

Related errors


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