{"record":{"id":"169d1ca67003e4c3","repo":"apache/pulsar","slug":"the-message-payload-processor-class-s-does-not-im","errorCode":null,"errorMessage":"The message payload processor class %s does not implement the desired constructor.","messagePattern":"The message payload processor class (.+?) does not implement the desired constructor\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/ValidatorUtils.java","lineNumber":132,"sourceCode":"                    String.format(\"The message payload processor class %s does not exist\", payloadProcessorClassName));\n        }\n        if (!payloadProcessorClass.asErasure().isAssignableTo(MessagePayloadProcessor.class)) {\n            throw new IllegalArgumentException(String.format(\"%s does not implement %s\", payloadProcessorClassName,\n                    MessagePayloadProcessor.class.getName()));\n        }\n\n        boolean hasConstructor;\n        if (conf.getConfig() == null || conf.getConfig().isEmpty()) {\n            hasConstructor = payloadProcessorClass.getDeclaredMethods().stream()\n                    .anyMatch(method -> method.isConstructor() && method.getParameters().size() == 0);\n        } else {\n            hasConstructor = payloadProcessorClass.getDeclaredMethods().stream()\n                    .anyMatch(method -> method.isConstructor() && method.getParameters().size() == 1\n                            && method.getParameters().get(0).getType().asErasure().represents(Map.class));\n        }\n\n        if (!hasConstructor) {\n            throw new IllegalArgumentException(\n                    String.format(\"The message payload processor class %s does not implement the desired constructor.\",\n                            conf.getClassName()));\n        }\n    }\n\n    public static void validateSerde(String inputSerializer, TypeDefinition typeArg, TypePool typePool,\n                                     boolean deser) {\n        if (isEmpty(inputSerializer)) {\n            return;\n        }\n        if (inputSerializer.equals(DEFAULT_SERDE)) {\n            return;\n        }\n        TypeDescription serdeClass;\n        try {\n            serdeClass = typePool.describe(inputSerializer).resolve();\n        } catch (TypePool.Resolution.NoSuchTypeException e) {\n            throw new IllegalArgumentException(","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/ValidatorUtils.java#L114-L150","documentation":"Thrown by ValidatorUtils.validateMessagePayloadProcessor when a configured MessagePayloadProcessor class does not declare the constructor required by its config. If MessagePayloadProcessorConfig.config is null or empty, the class must have a no-arg constructor; otherwise it must have a single-argument constructor taking a java.util.Map. The check runs at function-submission time via ByteBuddy's TypePool (bytecode inspection, no instantiation).","triggerScenarios":"Calling validateMessagePayloadProcessor(conf, typePool) where conf.getClassName() resolves to a MessagePayloadProcessor subclass, the class exists and implements MessagePayloadProcessor, but: (a) conf.getConfig() is null/empty and the class has no declared no-arg constructor, or (b) conf.getConfig() is non-empty and the class lacks a constructor with exactly one Map parameter. Note the check inspects getDeclaredMethods(), so inherited constructors do not count.","commonSituations":"Declaring a payload processor that only has a constructor taking custom types (e.g. a String) while supplying config; supplying windowConfig config properties but the processor only defines a no-arg constructor; the processor relying on its superclass's constructor; Lombok or other annotation processors removing/altering the implicit default constructor.","solutions":["If you pass no config, add an explicit public no-arg constructor to the MessagePayloadProcessor class.","If you pass config (conf.setConfig(...)), add a public constructor accepting a single java.util.Map<String, Object> (or Map<String, String>) parameter.","Ensure the constructor is declared directly on the class, not inherited from a superclass.","If the constructor exists but has a subtypes of Map parameter, change it to take exactly java.util.Map (raw or matching erasure) as its sole parameter."],"exampleFix":"// before: processor with config supplied but no matching constructor\npublic class MyProcessor implements MessagePayloadProcessor {\n    public MyProcessor(String topic) { ... }\n}\n// after\npublic class MyProcessor implements MessagePayloadProcessor {\n    public MyProcessor() { ... }\n    public MyProcessor(Map<String, Object> config) { ... }\n}","handlingStrategy":"validation","validationCode":"static boolean hasDesiredConstructor(String className, Map<String, String> config, ClassLoader cl) {\n    try {\n        Class<?> c = Class.forName(className, false, cl);\n        boolean wantsMap = config != null && !config.isEmpty();\n        for (Constructor<?> ctor : c.getDeclaredConstructors()) {\n            if (wantsMap) {\n                if (ctor.getParameterCount() == 1 && Map.class.isAssignableFrom(ctor.getParameterTypes()[0])) return true;\n            } else if (ctor.getParameterCount() == 0) {\n                return true;\n            }\n        }\n        return false;\n    } catch (ClassNotFoundException e) { return false; }\n}","typeGuard":"static boolean isMessagePayloadProcessorWithCtor(Class<?> c, boolean withConfig) {\n    return MessagePayloadProcessor.class.isAssignableFrom(c)\n        && (withConfig\n            ? java.util.Arrays.stream(c.getDeclaredConstructors()).anyMatch(k -> k.getParameterCount() == 1 && Map.class.isAssignableFrom(k.getParameterTypes()[0]))\n            : java.util.Arrays.stream(c.getDeclaredConstructors()).anyMatch(k -> k.getParameterCount() == 0));\n}","tryCatchPattern":"try {\n    functionConfigBuilder.validate();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"does not implement the desired constructor\")) {\n        log.error(\"Payload processor {} needs a {} constructor\", className, configEmpty ? \"no-arg\" : \"Map\", e);\n    }\n    throw e;\n}","preventionTips":["Always declare an explicit public no-arg constructor AND a Map constructor on payload processor classes.","Remember getDeclaredMethods() ignores inherited constructors — do not rely on a superclass's constructor.","Keep the constructor style in sync with whether you set config on MessagePayloadProcessorConfig."],"tags":["java","pulsar-functions","configuration","constructor-validation"],"backgroundTag":"missing-required-constructor","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"}