{"record":{"id":"916fbe5ad753f6c5","repo":"apache/pulsar","slug":"only-one-of-schematype-or-serdeclassname-should-be","errorCode":null,"errorMessage":"Only one of schemaType or serdeClassName should be set in inputSpec","messagePattern":"Only one of schemaType or serdeClassName should be set in inputSpec","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":694,"sourceCode":"                try {\n                    consumerConfig = OBJECT_MAPPER.readValue(conf, ConsumerConfig.class);\n                } catch (JsonProcessingException e) {\n                    throw new IllegalArgumentException(\n                            String.format(\"Topic %s has an incorrect schema Info\", topicName));\n                }\n                ValidatorUtils.validateSchema(consumerConfig.getSchemaType(), typeArgs[0],\n                        validatableFunctionPackage.getTypePool(), true);\n            });\n        }\n\n        // Check if the Input serialization/deserialization class exists in jar or already loaded and that it\n        // implements Schema or SerDe classes\n\n        if (functionConfig.getInputSpecs() != null) {\n            functionConfig.getInputSpecs().forEach((topicName, conf) -> {\n                // Need to make sure that one and only one of schema/serde is set\n                if (!isEmpty(conf.getSchemaType()) && !isEmpty(conf.getSerdeClassName())) {\n                    throw new IllegalArgumentException(\n                        \"Only one of schemaType or serdeClassName should be set in inputSpec\");\n                }\n                if (!isEmpty(conf.getSerdeClassName())) {\n                    ValidatorUtils.validateSerde(conf.getSerdeClassName(), typeArgs[0],\n                            validatableFunctionPackage.getTypePool(), true);\n                }\n                if (!isEmpty(conf.getSchemaType())) {\n                    ValidatorUtils.validateSchema(conf.getSchemaType(), typeArgs[0],\n                            validatableFunctionPackage.getTypePool(), true);\n                }\n                if (conf.getCryptoConfig() != null) {\n                    ValidatorUtils.validateCryptoKeyReader(conf.getCryptoConfig(),\n                            validatableFunctionPackage.getTypePool(), false);\n                }\n                if (conf.getMessagePayloadProcessorConfig() != null) {\n                    ValidatorUtils.validateMessagePayloadProcessor(conf.getMessagePayloadProcessorConfig(),\n                            validatableFunctionPackage.getTypePool());\n                }","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L676-L712","documentation":"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.","triggerScenarios":"FunctionConfig inputSpecs containing an InputSpec with both setSchemaType(...) and setSerdeClassName(...) populated for the same topic, typically from merged/legacy configs.","commonSituations":"Upgrading from old SerDe-based configs to schema-based configs leaving both fields set; copy-pasted config templates combining both styles.","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"],"exampleFix":"// before\ninputSpec.setSchemaType(\"avro\");\ninputSpec.setSerdeClassName(\"com.example.MySerde\");\n// after\ninputSpec.setSchemaType(\"avro\");","handlingStrategy":"validation","validationCode":"config.getInputSpecs().forEach((topic, spec) -> { if (notBlank(spec.getSchemaType()) && notBlank(spec.getSerdeClassName())) throw new IllegalArgumentException(\"topic \" + topic + \" sets both schemaType and serdeClassName\"); });","typeGuard":"boolean exclusiveSerializer(InputSpec s) { return isBlank(s.getSchemaType()) || isBlank(s.getSerdeClassName()); }","tryCatchPattern":"try { ... } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"Only one of schemaType or serdeClassName\")) { log.error(\"Clear one serializer field in inputSpecs\"); } }","preventionTips":["Prefer schemaType; leave serdeClassName unset in new configs","Clear legacy serde fields during migrations","Add config linting that enforces one-serializer-per-input"],"tags":["pulsar","functions","schema","configuration"],"backgroundTag":"mutually-exclusive-config-options","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}