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
- 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.
- Change the SerDe's type parameter so it implements SerDe<YourInputType>.
- 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
- Declare the function's input type and the SerDe's generic from the same class constant to keep them in lockstep.
- Regenerate both the POJO and its SerDe together whenever the message class changes.
- Avoid generic SerDe<Object> when the function input is a concrete type.
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
- Serializer type mismatch ${typeArg} vs ${serDeTypeArg}
- Inconsistent types found between function input type and tim
- Sink transform function output must be of type Record
- The input serialization/deserialization class %s does not ex
- Schema type mismatch ${typeArg} vs ${schemaTypeArg}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/b2cf4c601b108ee2.
Report an issue: GitHub.