apache/pulsar · error · IllegalArgumentException
Only one of serdeClassName or schemaType should be set
Error message
Only one of serdeClassName or schemaType should be set
What it means
For each entry in sinkConfig.inputSpecs, the consumer config may specify either the legacy serdeClassName or the newer schemaType, but not both. The library throws when both are non-empty for the same topic consumer.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java:537
if (sinkConfig.getTopicToSerdeClassName() != null) {
for (String serdeClassName : sinkConfig.getTopicToSerdeClassName().values()) {
ValidatorUtils.validateSerde(serdeClassName, typeArg, inputFunction.getTypePool(), true);
}
}
if (sinkConfig.getTopicToSchemaType() != null) {
for (String schemaType : sinkConfig.getTopicToSchemaType().values()) {
ValidatorUtils.validateSchema(schemaType, typeArg, inputFunction.getTypePool(), true);
}
}
// topicsPattern does not need checks
if (sinkConfig.getInputSpecs() != null) {
for (ConsumerConfig consumerSpec : sinkConfig.getInputSpecs().values()) {
// Only one is set
if (!isEmpty(consumerSpec.getSerdeClassName()) && !isEmpty(consumerSpec.getSchemaType())) {
throw new IllegalArgumentException("Only one of serdeClassName or schemaType should be set");
}
if (!isEmpty(consumerSpec.getSerdeClassName())) {
ValidatorUtils.validateSerde(consumerSpec.getSerdeClassName(), typeArg,
inputFunction.getTypePool(), true);
}
if (!isEmpty(consumerSpec.getSchemaType())) {
ValidatorUtils.validateSchema(consumerSpec.getSchemaType(), typeArg,
inputFunction.getTypePool(), true);
}
if (consumerSpec.getCryptoConfig() != null) {
ValidatorUtils.validateCryptoKeyReader(consumerSpec.getCryptoConfig(),
inputFunction.getTypePool(), false);
}
if (consumerSpec.getMessagePayloadProcessorConfig() != null) {
ValidatorUtils.validateMessagePayloadProcessor(consumerSpec.getMessagePayloadProcessorConfig(),
inputFunction.getTypePool());
}
}View on GitHub (pinned to 820761864e)
Solutions
- Remove serdeClassName from the ConsumerConfig if using schemas
- Or remove schemaType and keep the legacy serde
- Audit inputSpecs for any ConsumerConfig with both fields set
Example fix
// before
ConsumerConfig c = new ConsumerConfig();
c.setSerdeClassName("org.example.StringSerde");
c.setSchemaType("STRING");
// after
ConsumerConfig c = new ConsumerConfig();
c.setSchemaType("STRING"); Defensive patterns
Strategy: validation
Validate before calling
if (config.getInputSpecs() != null) {
for (ConsumerConfig c : config.getInputSpecs().values()) {
if (c.getSerdeClassName() != null && !c.getSerdeClassName().isEmpty()
&& c.getSchemaType() != null && !c.getSchemaType().isEmpty()) {
throw new IllegalStateException("Set only serdeClassName or schemaType per consumer");
}
}
} Try / catch
try {
admin.sinks().createSink(config, archive);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("serdeClassName or schemaType")) {
config.getInputSpecs().values().forEach(c -> c.setSerdeClassName(null));
} else { throw e; }
} Prevention
- During serde->schema migration, delete serdeClassName instead of commenting it
- Sanitize configs when copying between environments
When it happens
Trigger: SinkConfig.inputSpecs containing a ConsumerConfig with both serdeClassName and schemaType populated (e.g. after migrating a config from serde to schema and forgetting to remove the old field).
Common situations: Migrating configs created pre-2.x (serde) to schema-based configs; copying config examples that set both fields; merging configs from two sources.
Related errors
- When effectively once processing guarantee is specified, ret
- Only one of retain ordering or retain key ordering can be se
- Sink class %s does not implement the correct interface
- Function config is not provided
- Output topic %s is also being used as an input topic (topics
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/e43491205abd3eef.
Report an issue: GitHub.