apache/pulsar · error · IllegalArgumentException

The crypto key reader class %s does not implement the desire

Error message

The crypto key reader class %s does not implement the desired constructor.

What it means

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.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/ValidatorUtils.java:93

        String cryptoClassName = conf.getCryptoKeyReaderClassName();
        TypeDescription cryptoClass = null;
        try {
            cryptoClass = typePool.describe(cryptoClassName).resolve();
        } catch (TypePool.Resolution.NoSuchTypeException e) {
            throw new IllegalArgumentException(
                    String.format("The crypto key reader class %s does not exist", cryptoClassName));
        }
        if (!cryptoClass.asErasure().isAssignableTo(CryptoKeyReader.class)) {
            throw new IllegalArgumentException(
                    String.format("%s does not implement %s", cryptoClassName, CryptoKeyReader.class.getName()));
        }

        boolean hasConstructor = cryptoClass.getDeclaredMethods().stream()
                .anyMatch(method -> method.isConstructor() && method.getParameters().size() == 1
                        && method.getParameters().get(0).getType().asErasure().represents(Map.class));

        if (!hasConstructor) {
            throw new IllegalArgumentException(
                    String.format("The crypto key reader class %s does not implement the desired constructor.",
                            conf.getCryptoKeyReaderClassName()));
        }

        if (isProducer && (conf.getEncryptionKeys() == null || conf.getEncryptionKeys().length == 0)) {
            throw new IllegalArgumentException("Missing encryption key name for producer crypto key reader");
        }
    }

    public static void validateMessagePayloadProcessor(MessagePayloadProcessorConfig conf, TypePool typePool) {
        if (isEmpty(conf.getClassName())) {
            return;
        }

        String payloadProcessorClassName = conf.getClassName();
        TypeDescription payloadProcessorClass = null;
        try {
            payloadProcessorClass = typePool.describe(payloadProcessorClassName).resolve();

View on GitHub (pinned to 820761864e)

Solutions

  1. Add a public constructor accepting a single java.util.Map parameter (often Map<String, Object>)
  2. Alternatively add a no-arg constructor plus the Map constructor if used in both contexts
  3. Ensure the constructor is not private and is declared on the class (not only inherited)

Example fix

// before
public class MyKeyReader implements CryptoKeyReader {
    public MyKeyReader() { ... }
}
// after
public class MyKeyReader implements CryptoKeyReader {
    public MyKeyReader() { this(Collections.emptyMap()); }
    public MyKeyReader(Map<String, Object> config) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

boolean hasMapCtor = Arrays.stream(cryptoClass.getConstructors())
        .anyMatch(c -> c.getParameterCount() == 1 && Map.class.isAssignableFrom(c.getParameterTypes()[0]));
if (!hasMapCtor) throw new IllegalStateException("Add a Map-arg constructor to " + cryptoClass.getName());

Try / catch

try {
    ValidatorUtils.validateCryptoKeyReader(conf, typePool, isProducer);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("desired constructor")) {
        // add public MyKeyReader(Map<String, Object> config) { ... }
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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