apache/pulsar · error · IllegalArgumentException

CryptoKeyReader class name required

Error message

CryptoKeyReader class name required

What it means

If an InputSpec for a topic carries a CryptoConfig (input decryption), the cryptoKeyReaderClassName must be a non-blank string naming a class that implements CryptoKeyReader. A blank/null name means Pulsar could not decrypt messages, so doCommonChecks throws IllegalArgumentException('CryptoKeyReader class name required').

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:924

            if (filename.contains("..")) {
                throw new IllegalArgumentException("Invalid filename: " + filename);
            }

            if (!new File(filename).exists()) {
                throw new IllegalArgumentException("The supplied go file does not exist");
            }
        }

        if (functionConfig.getInputSpecs() != null) {
            functionConfig.getInputSpecs().forEach((topicName, conf) -> {
                // receiver queue size should be >= 0
                if (conf.getReceiverQueueSize() != null && conf.getReceiverQueueSize() < 0) {
                    throw new IllegalArgumentException(
                        "Receiver queue size should be >= zero");
                }

                if (conf.getCryptoConfig() != null && isBlank(conf.getCryptoConfig().getCryptoKeyReaderClassName())) {
                    throw new IllegalArgumentException(
                            "CryptoKeyReader class name required");
                }
                if (conf.getMessagePayloadProcessorConfig() != null && isBlank(
                        conf.getMessagePayloadProcessorConfig().getClassName())) {
                    throw new IllegalArgumentException(
                            "MessagePayloadProcessor class name required");
                }
            });
        }

        if (functionConfig.getProducerConfig() != null
                && functionConfig.getProducerConfig().getCryptoConfig() != null) {
            if (isBlank(functionConfig.getProducerConfig().getCryptoConfig().getCryptoKeyReaderClassName())) {
                throw new IllegalArgumentException("CryptoKeyReader class name required");
            }

            if (functionConfig.getProducerConfig().getCryptoConfig().getEncryptionKeys() == null
                    || functionConfig.getProducerConfig().getCryptoConfig().getEncryptionKeys().length == 0) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Set the fully qualified class name of your CryptoKeyReader implementation: cryptoConfig.setCryptoKeyReaderClassName("com.example.MyKeyReader")
  2. Ensure the implementation class is available on the function worker/instance classpath (nar/jar shipping)
  3. If input decryption is not needed, remove the CryptoConfig from the InputSpec entirely
  4. Check that the property/env substitution feeding the class name actually resolves to a non-blank value

Example fix

// before
CryptoConfig crypto = new CryptoConfig(); // className blank
inputSpec.setCryptoConfig(crypto);
// after
CryptoConfig crypto = new CryptoConfig();
crypto.setCryptoKeyReaderClassName("com.example.MyCryptoKeyReader");
inputSpec.setCryptoConfig(crypto);
Defensive patterns

Strategy: validation

Validate before calling

if (spec.getCryptoConfig() != null
        && (spec.getCryptoConfig().getCryptoKeyReaderClassName() == null
            || spec.getCryptoConfig().getCryptoKeyReaderClassName().isBlank())) {
    throw new IllegalStateException("cryptoKeyReaderClassName required for input topic " + topic);
}

Type guard

boolean hasCryptoKeyReader(org.apache.pulsar.functions.proto.Function.CryptoConfig c) {
    return c == null || (c.getCryptoKeyReaderClassName() != null
        && !c.getCryptoKeyReaderClassName().isBlank());
}

Try / catch

try {
    admin.functions().createFunction(conf);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("CryptoKeyReader class name")) {
        conf.getInputSpecs().values().forEach(s -> {
            if (s.getCryptoConfig() != null && s.getCryptoConfig().getCryptoKeyReaderClassName().isBlank()) {
                s.getCryptoConfig().setCryptoKeyReaderClassName(DEFAULT_KEY_READER_CLASS);
            }
        });
        admin.functions().createFunction(conf);
    } else throw e;
}

Prevention

When it happens

Trigger: Setting InputSpec.setCryptoConfig(new CryptoConfig()) (or with setCryptoKeyReaderClassName("")/whitespace) on any function input topic, then calling createFunction/updateFunction.

Common situations: Enabling input decryption but forgetting to point at the key reader implementation; crypto config copied from producer config where the key was set elsewhere; blank string produced by environment-variable substitution that failed; class name left as template placeholder.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/f4fe3d13bd5a5dc6. Report an issue: GitHub.