apache/pulsar · error · IllegalArgumentException
Only one of schemaType or serdeClassName should be set in in
Error message
Only one of schemaType or serdeClassName should be set in inputSpec
What it means
Each input spec must set at most one of schemaType and serdeClassName, since they are competing ways to declare the input deserializer. doJavaChecks throws IllegalArgumentException when both are non-empty for the same topic.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:694
try {
consumerConfig = OBJECT_MAPPER.readValue(conf, ConsumerConfig.class);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(
String.format("Topic %s has an incorrect schema Info", topicName));
}
ValidatorUtils.validateSchema(consumerConfig.getSchemaType(), typeArgs[0],
validatableFunctionPackage.getTypePool(), true);
});
}
// Check if the Input serialization/deserialization class exists in jar or already loaded and that it
// implements Schema or SerDe classes
if (functionConfig.getInputSpecs() != null) {
functionConfig.getInputSpecs().forEach((topicName, conf) -> {
// Need to make sure that one and only one of schema/serde is set
if (!isEmpty(conf.getSchemaType()) && !isEmpty(conf.getSerdeClassName())) {
throw new IllegalArgumentException(
"Only one of schemaType or serdeClassName should be set in inputSpec");
}
if (!isEmpty(conf.getSerdeClassName())) {
ValidatorUtils.validateSerde(conf.getSerdeClassName(), typeArgs[0],
validatableFunctionPackage.getTypePool(), true);
}
if (!isEmpty(conf.getSchemaType())) {
ValidatorUtils.validateSchema(conf.getSchemaType(), typeArgs[0],
validatableFunctionPackage.getTypePool(), true);
}
if (conf.getCryptoConfig() != null) {
ValidatorUtils.validateCryptoKeyReader(conf.getCryptoConfig(),
validatableFunctionPackage.getTypePool(), false);
}
if (conf.getMessagePayloadProcessorConfig() != null) {
ValidatorUtils.validateMessagePayloadProcessor(conf.getMessagePayloadProcessorConfig(),
validatableFunctionPackage.getTypePool());
}View on GitHub (pinned to 820761864e)
Solutions
- Remove serdeClassName and keep schemaType (recommended)
- Or remove schemaType and keep serdeClassName for legacy SerDe usage
- Audit inputSpecs programmatically before submission to ensure exclusivity
Example fix
// before
inputSpec.setSchemaType("avro");
inputSpec.setSerdeClassName("com.example.MySerde");
// after
inputSpec.setSchemaType("avro"); Defensive patterns
Strategy: validation
Validate before calling
config.getInputSpecs().forEach((topic, spec) -> { if (notBlank(spec.getSchemaType()) && notBlank(spec.getSerdeClassName())) throw new IllegalArgumentException("topic " + topic + " sets both schemaType and serdeClassName"); }); Type guard
boolean exclusiveSerializer(InputSpec s) { return isBlank(s.getSchemaType()) || isBlank(s.getSerdeClassName()); } Try / catch
try { ... } catch (IllegalArgumentException e) { if (e.getMessage().contains("Only one of schemaType or serdeClassName")) { log.error("Clear one serializer field in inputSpecs"); } } Prevention
- Prefer schemaType; leave serdeClassName unset in new configs
- Clear legacy serde fields during migrations
- Add config linting that enforces one-serializer-per-input
When it happens
Trigger: FunctionConfig inputSpecs containing an InputSpec with both setSchemaType(...) and setSerdeClassName(...) populated for the same topic, typically from merged/legacy configs.
Common situations: Upgrading from old SerDe-based configs to schema-based configs leaving both fields set; copy-pasted config templates combining both styles.
Related errors
- Only one of outputSchemaType or outputSerdeClassName should
- When Guarantees == ATMOST_ONCE, autoAck must be equal to tru
- Function class name is not provided.
- Topic %s has an incorrect schema Info
- Function tenant cannot be null
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/916fbe5ad753f6c5.
Report an issue: GitHub.