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

  1. Remove serdeClassName from the ConsumerConfig if using schemas
  2. Or remove schemaType and keep the legacy serde
  3. 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

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


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