{"record":{"id":"e43491205abd3eef","repo":"apache/pulsar","slug":"only-one-of-serdeclassname-or-schematype-should-be","errorCode":null,"errorMessage":"Only one of serdeClassName or schemaType should be set","messagePattern":"Only one of serdeClassName or schemaType should be set","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java","lineNumber":537,"sourceCode":"        if (sinkConfig.getTopicToSerdeClassName() != null) {\n            for (String serdeClassName : sinkConfig.getTopicToSerdeClassName().values()) {\n                ValidatorUtils.validateSerde(serdeClassName, typeArg, inputFunction.getTypePool(), true);\n            }\n        }\n\n        if (sinkConfig.getTopicToSchemaType() != null) {\n            for (String schemaType : sinkConfig.getTopicToSchemaType().values()) {\n                ValidatorUtils.validateSchema(schemaType, typeArg, inputFunction.getTypePool(), true);\n            }\n        }\n\n        // topicsPattern does not need checks\n\n        if (sinkConfig.getInputSpecs() != null) {\n            for (ConsumerConfig consumerSpec : sinkConfig.getInputSpecs().values()) {\n                // Only one is set\n                if (!isEmpty(consumerSpec.getSerdeClassName()) && !isEmpty(consumerSpec.getSchemaType())) {\n                    throw new IllegalArgumentException(\"Only one of serdeClassName or schemaType should be set\");\n                }\n                if (!isEmpty(consumerSpec.getSerdeClassName())) {\n                    ValidatorUtils.validateSerde(consumerSpec.getSerdeClassName(), typeArg,\n                            inputFunction.getTypePool(), true);\n                }\n                if (!isEmpty(consumerSpec.getSchemaType())) {\n                    ValidatorUtils.validateSchema(consumerSpec.getSchemaType(), typeArg,\n                            inputFunction.getTypePool(), true);\n                }\n                if (consumerSpec.getCryptoConfig() != null) {\n                    ValidatorUtils.validateCryptoKeyReader(consumerSpec.getCryptoConfig(),\n                            inputFunction.getTypePool(), false);\n                }\n                if (consumerSpec.getMessagePayloadProcessorConfig() != null) {\n                    ValidatorUtils.validateMessagePayloadProcessor(consumerSpec.getMessagePayloadProcessorConfig(),\n                            inputFunction.getTypePool());\n                }\n            }","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java#L519-L555","documentation":"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.","triggerScenarios":"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).","commonSituations":"Migrating configs created pre-2.x (serde) to schema-based configs; copying config examples that set both fields; merging configs from two sources.","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"],"exampleFix":"// before\nConsumerConfig c = new ConsumerConfig();\nc.setSerdeClassName(\"org.example.StringSerde\");\nc.setSchemaType(\"STRING\");\n// after\nConsumerConfig c = new ConsumerConfig();\nc.setSchemaType(\"STRING\");","handlingStrategy":"validation","validationCode":"if (config.getInputSpecs() != null) {\n  for (ConsumerConfig c : config.getInputSpecs().values()) {\n    if (c.getSerdeClassName() != null && !c.getSerdeClassName().isEmpty()\n        && c.getSchemaType() != null && !c.getSchemaType().isEmpty()) {\n      throw new IllegalStateException(\"Set only serdeClassName or schemaType per consumer\");\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  admin.sinks().createSink(config, archive);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"serdeClassName or schemaType\")) {\n    config.getInputSpecs().values().forEach(c -> c.setSerdeClassName(null));\n  } else { throw e; }\n}","preventionTips":["During serde->schema migration, delete serdeClassName instead of commenting it","Sanitize configs when copying between environments"],"tags":["pulsar-functions","sink","configuration","validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}