apache/pulsar · error · IllegalArgumentException
Output Serde mismatch
Error message
Output Serde mismatch
What it means
The output serialization class of a function is immutable on update. If the new config supplies a non-empty outputSerdeClassName that differs from the existing one, validateUpdate rejects the update, since changing the output serializer would break the declared output schema of the existing subscription.
Solutions
- Omit outputSerdeClassName in the update (leave empty) to keep the existing value, or pass the identical existing value.
- To change the output serde, delete and recreate the function with the new serde.
- Check that both configs use the same fully qualified serde class name (package differences also count as mismatch).
Example fix
// before
newConfig.setOutputSerdeClassName("org.apache.pulsar.functions.api.examples.JsonSerde"); // existing: ...StringSerde
// after
newConfig.setOutputSerdeClassName(""); // keep existing serde on update
// or recreate the function with the new serde Defensive patterns
Strategy: validation
Validate before calling
if (newConfig.getOutputSerdeClassName() != null && !newConfig.getOutputSerdeClassName().isEmpty()
&& !newConfig.getOutputSerdeClassName().equals(existingConfig.getOutputSerdeClassName())) {
throw new IllegalArgumentException("outputSerdeClassName is immutable on update");
} Type guard
boolean outputSerdeUnchanged(FunctionConfig existing, FunctionConfig updated) {
String n = updated.getOutputSerdeClassName();
return n == null || n.isEmpty() || n.equals(existing.getOutputSerdeClassName());
} Try / catch
try {
merged = FunctionConfigUtils.validateUpdate(existing, updated);
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Output Serde mismatch")) {
updated.setOutputSerdeClassName("");
merged = FunctionConfigUtils.validateUpdate(existing, updated);
} else { throw e; }
} Prevention
- Leave outputSerdeClassName empty in update payloads to keep the existing value.
- Use schemaType-based config consistently instead of mixing serdeClassName.
- Recreate the function to change output serialization.
When it happens
Trigger: Updating a function with newConfig.getOutputSerdeClassName() non-empty and != existingConfig.getOutputSerdeClassName().
Common situations: Switching SerDe classes (e.g. from StringSerde to JSONSchema-based serde) on a live function; config template changes; fixing a wrong serde name via update instead of recreate.
Related errors
- Function Names differ
- Function Names differ
- Input Topics cannot be altered
- isRegexPattern for input topic
- Namespaces differ
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/f70a9ca30102f4e8.
Report an issue: GitHub.
Appendix: source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:1056
.isRegexPattern(false)
.build());
});
}
if (!newConfig.getInputSpecs().isEmpty()) {
newConfig.getInputSpecs().forEach((topicName, consumerConfig) -> {
if (!existingConfig.getInputSpecs().containsKey(topicName)) {
throw new IllegalArgumentException("Input Topics cannot be altered");
}
if (consumerConfig.isRegexPattern() != existingConfig.getInputSpecs().get(topicName).isRegexPattern()) {
throw new IllegalArgumentException(
"isRegexPattern for input topic " + topicName + " cannot be altered");
}
mergedConfig.getInputSpecs().put(topicName, consumerConfig);
});
}
if (!StringUtils.isEmpty(newConfig.getOutputSerdeClassName()) && !newConfig.getOutputSerdeClassName()
.equals(existingConfig.getOutputSerdeClassName())) {
throw new IllegalArgumentException("Output Serde mismatch");
}
if (!StringUtils.isEmpty(newConfig.getOutputSchemaType()) && !newConfig.getOutputSchemaType()
.equals(existingConfig.getOutputSchemaType())) {
throw new IllegalArgumentException("Output Schema mismatch");
}
if (!StringUtils.isEmpty(newConfig.getLogTopic())) {
mergedConfig.setLogTopic(newConfig.getLogTopic());
}
if (newConfig.getProcessingGuarantees() != null && !newConfig.getProcessingGuarantees()
.equals(existingConfig.getProcessingGuarantees())) {
throw new IllegalArgumentException("Processing Guarantees cannot be altered");
}
if (newConfig.getRetainOrdering() != null && !newConfig.getRetainOrdering()
.equals(existingConfig.getRetainOrdering())) {
throw new IllegalArgumentException("Retain Ordering cannot be altered");
}
if (newConfig.getRetainKeyOrdering() != null && !newConfig.getRetainKeyOrdering()
.equals(existingConfig.getRetainKeyOrdering())) {View on GitHub (pinned to 820761864e)