{"record":{"id":"daeabac9cf4b007f","repo":"apache/pulsar","slug":"must-provide-encryption-key-name-for-crypto-key-re","errorCode":null,"errorMessage":"Must provide encryption key name for crypto key reader","messagePattern":"Must provide encryption key name for crypto key reader","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":943,"sourceCode":"                            \"CryptoKeyReader class name required\");\n                }\n                if (conf.getMessagePayloadProcessorConfig() != null && isBlank(\n                        conf.getMessagePayloadProcessorConfig().getClassName())) {\n                    throw new IllegalArgumentException(\n                            \"MessagePayloadProcessor class name required\");\n                }\n            });\n        }\n\n        if (functionConfig.getProducerConfig() != null\n                && functionConfig.getProducerConfig().getCryptoConfig() != null) {\n            if (isBlank(functionConfig.getProducerConfig().getCryptoConfig().getCryptoKeyReaderClassName())) {\n                throw new IllegalArgumentException(\"CryptoKeyReader class name required\");\n            }\n\n            if (functionConfig.getProducerConfig().getCryptoConfig().getEncryptionKeys() == null\n                    || functionConfig.getProducerConfig().getCryptoConfig().getEncryptionKeys().length == 0) {\n                throw new IllegalArgumentException(\"Must provide encryption key name for crypto key reader\");\n            }\n        }\n    }\n\n    public static Collection<String> collectAllInputTopics(FunctionConfig functionConfig) {\n        List<String> retval = new LinkedList<>();\n        if (functionConfig.getInputs() != null) {\n            retval.addAll(functionConfig.getInputs());\n        }\n        if (functionConfig.getTopicsPattern() != null) {\n            retval.add(functionConfig.getTopicsPattern());\n        }\n        if (functionConfig.getCustomSerdeInputs() != null) {\n            retval.addAll(functionConfig.getCustomSerdeInputs().keySet());\n        }\n        if (functionConfig.getCustomSchemaInputs() != null) {\n            retval.addAll(functionConfig.getCustomSchemaInputs().keySet());\n        }","sourceCodeStart":925,"sourceCodeEnd":961,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L925-L961","documentation":"Pulsar Functions validate that, when producer-side encryption is configured, a CryptoKeyReader implementation class AND at least one encryption key name are both set. This error means cryptoConfig was provided with a cryptoKeyReaderClassName but the encryptionKeys array is null or empty, so the key reader would have no keys to supply to the broker. It is thrown by doCommonChecks during function validation.","triggerScenarios":"Calling function update/create APIs (via validateNonJavaFunction or validateJavaFunction) with a FunctionConfig whose producerConfig.cryptoConfig.cryptoKeyReaderClassName is set but producerConfig.cryptoConfig.encryptionKeys is null or an empty array.","commonSituations":"Developers enable end-to-end encryption by copying a config snippet that sets the key reader class but forget to list the encryption key names (e.g. my-app-key) under encryptionKeys; or keys are removed from a YAML/JSON config leaving an empty list while the reader class remains.","solutions":["Add the encryption key names to producerConfig.cryptoConfig.encryptionKeys, e.g. setEncryptionKeys(new String[]{\"my-app-key\"}).","If encryption is not intended, remove the cryptoConfig section entirely instead of keeping only the key reader class.","If keys are supplied via a different mechanism, ensure the config deserialization actually populates encryptionKeys (check YAML/JSON field names)."],"exampleFix":"// before\nproducerConfig.cryptoConfig.cryptoKeyReaderClassName = \"org.example.MyCryptoKeyReader\";\n// encryptionKeys missing\n// after\nproducerConfig.cryptoConfig.cryptoKeyReaderClassName = \"org.example.MyCryptoKeyReader\";\nproducerConfig.cryptoConfig.encryptionKeys = new String[]{\"my-app-key\"};","handlingStrategy":"validation","validationCode":"CryptoConfig cc = cfg.getProducerConfig().getCryptoConfig();\nif (cc != null && cc.getCryptoKeyReaderClassName() != null\n        && (cc.getEncryptionKeys() == null || cc.getEncryptionKeys().length == 0)) {\n    throw new IllegalArgumentException(\"encryptionKeys must be set when cryptoKeyReaderClassName is set\");\n}","typeGuard":"boolean hasEncryptionKeys(FunctionConfig c) {\n    return c.getProducerConfig() != null && c.getProducerConfig().getCryptoConfig() != null\n        && c.getProducerConfig().getCryptoConfig().getEncryptionKeys() != null\n        && c.getProducerConfig().getCryptoConfig().getEncryptionKeys().length > 0;\n}","tryCatchPattern":"try {\n    FunctionConfigUtils.validateJavaFunction(config, pkg);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"encryption key name\")) {\n        config.getProducerConfig().getCryptoConfig().setEncryptionKeys(new String[]{\"my-app-key\"});\n    } else { throw e; }\n}","preventionTips":["Always pair cryptoKeyReaderClassName with at least one entry in encryptionKeys.","Remove the whole cryptoConfig block when encryption is not needed.","Validate crypto config in CI before deploying function configs."],"tags":["pulsar-functions","config-validation","encryption","crypto"],"backgroundTag":"missing-required-config-field","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"}