apache/pulsar · error · RuntimeException

Inconsistent types found between function input type and tim

Error message

Inconsistent types found between function input type and timestamp extractor type:  function type = ${typeArgs0}, timestamp extractor type = ${timestampExtractorTypeArgs0}

What it means

WindowFunctionExecutor.getTimeStampExtractor validates that the generic input type of the user's Function class (typeArgs[0]) matches the generic type of the user-provided TimestampExtractor. Apache Pulsar throws this RuntimeException at window setup when TypeResolver resolves the raw generic arguments and the two classes differ, because events would otherwise be fed to the extractor with the wrong type.

Source

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

        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);
        }
        Class<?>[] timestampExtractorTypeArgs = TypeResolver.resolveRawArguments(
                TimestampExtractor.class, result.getClass());
        Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Function.class, this.getClass());
        if (!typeArgs[0].equals(timestampExtractorTypeArgs[0])) {
            throw new RuntimeException(
                    "Inconsistent types found between function input type and timestamp extractor type: "
                            + " function type = " + typeArgs[0] + ", timestamp extractor type = "
                            + timestampExtractorTypeArgs[0]);
        }
        return (TimestampExtractor<T>) result;
    }

    private TriggerPolicy<Record<T>, ?> getTriggerPolicy(WindowConfig windowConfig, WindowManager<Record<T>> manager,
                                                         EvictionPolicy<Record<T>, ?> evictionPolicy, Context context) {
        if (windowConfig.getSlidingIntervalCount() != null) {
            if (this.isEventTime()) {
                return new WatermarkCountTriggerPolicy<>(
                        windowConfig.getSlidingIntervalCount(), manager, evictionPolicy, manager);
            } else {
                return new CountTriggerPolicy<>(windowConfig.getSlidingIntervalCount(), manager, evictionPolicy);
            }
        } else {
            if (this.isEventTime()) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the TimestampExtractor's generic parameter exactly match the Function's input type parameter.
  2. Check the resolved classes by logging typeArgs[0] and timestampExtractorTypeArgs[0] and aligning the generics.
  3. If a raw type was intended, parameterize both classes explicitly instead of relying on raw types.

Example fix

// before
public class MyFunction implements Function<String, Void> { ... }
public class MyExtractor implements TimestampExtractor<Long> { ... }
// after
public class MyFunction implements Function<String, Void> { ... }
public class MyExtractor implements TimestampExtractor<String> { ... }
Defensive patterns

Strategy: validation

Validate before calling

Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Function.class, fnClass);
Class<?>[] teArgs = TypeResolver.resolveRawArguments(TimestampExtractor.class, extractorClass);
if (!typeArgs[0].equals(teArgs[0])) {
    throw new IllegalStateException("Function input " + typeArgs[0] + " != extractor type " + teArgs[0]);
}

Type guard

static <F, E> boolean typesMatch(Class<? extends Function<F, ?>> fn, Class<? extends TimestampExtractor<E>> te) {
    return TypeResolver.resolveRawArguments(Function.class, fn)[0]
        .equals(TypeResolver.resolveRawArguments(TimestampExtractor.class, te)[0]);
}

Prevention

When it happens

Trigger: Registering a TimestampExtractor whose generic parameter does not equal the Function's input generic, e.g. Function<String, ...> paired with TimestampExtractor<Long>, or leaving the extractor's type parameter untyped so resolution yields Object or a raw type.

Common situations: Copy-pasting a sample TimestampExtractor into a function with a different input POJO; refactoring the function input type without updating the extractor; using a raw (non-generic) extractor class that resolves to the wrong raw argument.

Related errors


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