apache/pulsar · error · IllegalArgumentException

Serializer type mismatch

Error message

Serializer type mismatch 

What it means

Thrown by ValidatorUtils.validateSerde when the SerDe's declared type parameter is not compatible with the function's input type argument (deser=true branch). For deserialization, the SerDe's type argument must be assignable TO the function's input type; otherwise the objects the SerDe produces would not match what the function expects.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/ValidatorUtils.java:163

        }
        TypeDescription serdeClass;
        try {
            serdeClass = typePool.describe(inputSerializer).resolve();
        } catch (TypePool.Resolution.NoSuchTypeException e) {
            throw new IllegalArgumentException(
                    String.format("The input serialization/deserialization class %s does not exist",
                            inputSerializer));
        }
        TypeDescription.Generic serDeTypeArg = serdeClass.getInterfaces().stream()
                .filter(i -> i.asErasure().isAssignableTo(SerDe.class))
                .findFirst()
                .map(i -> i.getTypeArguments().get(0))
                .orElseThrow(() -> new IllegalArgumentException(
                        String.format("%s does not implement %s", inputSerializer, SerDe.class.getName())));

        if (deser) {
            if (!serDeTypeArg.asErasure().isAssignableTo(typeArg.asErasure())) {
                throw new IllegalArgumentException("Serializer type mismatch " + typeArg.getActualName() + " vs "
                        + serDeTypeArg.getActualName());
            }
        } else {
            if (!serDeTypeArg.asErasure().isAssignableFrom(typeArg.asErasure())) {
                throw new IllegalArgumentException("Serializer type mismatch " + typeArg.getActualName() + " vs "
                        + serDeTypeArg.getActualName());
            }
        }
    }

    private static void validateSchemaType(TypeDefinition schema, TypeDefinition typeArg, TypePool typePool,
                                           boolean input) {

        TypeDescription.Generic schemaTypeArg = schema.getInterfaces().stream()
                .filter(i -> i.asErasure().isAssignableTo(Schema.class))
                .findFirst()
                .map(i -> i.getTypeArguments().get(0))
                .orElse(null);

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the function's input type argument match (or be a supertype of) the SerDe's type parameter — e.g. change the function signature to accept the SerDe's element type.
  2. Change the SerDe's type parameter so it implements SerDe<YourInputType>.
  3. Regenerate or update the SerDe when the message class changes so the generic type stays in sync.

Example fix

// before
public class EventSerde implements SerDe<OldEvent> { ... }
public class MyFunction implements Function<NewEvent, Void> { ... }
// after
public class EventSerde implements SerDe<NewEvent> { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

static boolean serdeMatchesInput(String serdeClass, Class<?> inputType, ClassLoader cl) {
    try {
        for (Type t : Class.forName(serdeClass, false, cl).getGenericInterfaces()) {
            if (t instanceof ParameterizedType
                    && ((ParameterizedType) t).getRawType() == SerDe.class) {
                Type arg = ((ParameterizedType) t).getActualTypeArguments()[0];
                return arg instanceof Class<?> && ((Class<?>) arg).isAssignableFrom(inputType);
            }
        }
        return false;
    } catch (ClassNotFoundException e) { return false; }
}

Type guard

static <T> boolean isSerDeFor(Class<? extends SerDe<T>> serdeClass, Class<T> inputType) {
    return SerDe.class.isAssignableFrom(serdeClass);
}

Try / catch

try {
    validateSerde(inputSerializer, typeArg, typePool, true);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Serializer type mismatch")) {
        throw new IllegalStateException("Input SerDe " + inputSerializer
            + " element type does not match function input type; align generics", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: validateSerde(inputSerializer, typeArg, typePool, true) where the SerDe class implements SerDe<T> and T.asErasure().isAssignableTo(typeArg.asErasure()) is false — e.g. the function's input type is MyEvent but the SerDe is SerDe<OtherEvent> or SerDe<Object> paired with a narrower input type.

Common situations: Reusing a SerDe written for a different message class after changing the function's input type; generic SerDe (SerDe<Object> or SerDe<byte[]>) paired with a typed function input; schema/tenant config drift after evolving the function signature; copy-pasted function configs pointing to the previous version's SerDe.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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