{"record":{"id":"134203e1d903cdc8","repo":"apache/pulsar","slug":"the-crypto-key-reader-class-s-does-not-implement","errorCode":null,"errorMessage":"The crypto key reader class %s does not implement the desired constructor.","messagePattern":"The crypto key reader 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":93,"sourceCode":"        String cryptoClassName = conf.getCryptoKeyReaderClassName();\n        TypeDescription cryptoClass = null;\n        try {\n            cryptoClass = typePool.describe(cryptoClassName).resolve();\n        } catch (TypePool.Resolution.NoSuchTypeException e) {\n            throw new IllegalArgumentException(\n                    String.format(\"The crypto key reader class %s does not exist\", cryptoClassName));\n        }\n        if (!cryptoClass.asErasure().isAssignableTo(CryptoKeyReader.class)) {\n            throw new IllegalArgumentException(\n                    String.format(\"%s does not implement %s\", cryptoClassName, CryptoKeyReader.class.getName()));\n        }\n\n        boolean hasConstructor = cryptoClass.getDeclaredMethods().stream()\n                .anyMatch(method -> method.isConstructor() && method.getParameters().size() == 1\n                        && method.getParameters().get(0).getType().asErasure().represents(Map.class));\n\n        if (!hasConstructor) {\n            throw new IllegalArgumentException(\n                    String.format(\"The crypto key reader class %s does not implement the desired constructor.\",\n                            conf.getCryptoKeyReaderClassName()));\n        }\n\n        if (isProducer && (conf.getEncryptionKeys() == null || conf.getEncryptionKeys().length == 0)) {\n            throw new IllegalArgumentException(\"Missing encryption key name for producer crypto key reader\");\n        }\n    }\n\n    public static void validateMessagePayloadProcessor(MessagePayloadProcessorConfig conf, TypePool typePool) {\n        if (isEmpty(conf.getClassName())) {\n            return;\n        }\n\n        String payloadProcessorClassName = conf.getClassName();\n        TypeDescription payloadProcessorClass = null;\n        try {\n            payloadProcessorClass = typePool.describe(payloadProcessorClassName).resolve();","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/ValidatorUtils.java#L75-L111","documentation":"Function crypto key readers must expose a constructor taking a single Map (the config map). After class/interface checks pass, validateCryptoKeyReader inspects declared constructors via ByteBuddy and throws this IllegalArgumentException if no Map-arg constructor exists.","triggerScenarios":"Providing a custom CryptoKeyReader implementation that only has a no-arg constructor or constructors with other signatures, while the function framework needs to instantiate it with a Map config.","commonSituations":"Writing a CryptoKeyReader the same way as for raw Pulsar clients (no-arg) and reusing it in functions; refactoring removed the Map constructor; constructor made private.","solutions":["Add a public constructor accepting a single java.util.Map parameter (often Map<String, Object>)","Alternatively add a no-arg constructor plus the Map constructor if used in both contexts","Ensure the constructor is not private and is declared on the class (not only inherited)"],"exampleFix":"// before\npublic class MyKeyReader implements CryptoKeyReader {\n    public MyKeyReader() { ... }\n}\n// after\npublic class MyKeyReader implements CryptoKeyReader {\n    public MyKeyReader() { this(Collections.emptyMap()); }\n    public MyKeyReader(Map<String, Object> config) { ... }\n}","handlingStrategy":"validation","validationCode":"boolean hasMapCtor = Arrays.stream(cryptoClass.getConstructors())\n        .anyMatch(c -> c.getParameterCount() == 1 && Map.class.isAssignableFrom(c.getParameterTypes()[0]));\nif (!hasMapCtor) throw new IllegalStateException(\"Add a Map-arg constructor to \" + cryptoClass.getName());","typeGuard":null,"tryCatchPattern":"try {\n    ValidatorUtils.validateCryptoKeyReader(conf, typePool, isProducer);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"desired constructor\")) {\n        // add public MyKeyReader(Map<String, Object> config) { ... }\n    }\n    throw e;\n}","preventionTips":["Always provide both a no-arg and a Map-arg constructor in custom key readers","Keep constructors public and on the class itself","Document the required constructor in your connector README"],"tags":["pulsar-functions","crypto","constructor","reflection"],"backgroundTag":"missing-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"}