apache/pulsar · error · RuntimeException

Failed to load crypto key reader class %sx

Error message

Failed to load crypto key reader class %sx

What it means

CryptoUtils.getCryptoKeyReaderInstance loads a CryptoKeyReader implementation class by name using the provided classloader and instantiates it. If ClassLoaderUtils.loadClass cannot find the class (ClassNotFoundException), it throws this RuntimeException — note the message appends a stray 'x' after the class name ('...class com.foo.MyReaderx').

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/CryptoUtils.java:97

            keyNames.add(spec.getProducerEncryptionKeyNameAt(i));
        }

        bldr.cryptoKeyReaderClassName(spec.getCryptoKeyReaderClassName())
                .cryptoKeyReaderConfig(cryptoReaderConfig)
                .consumerCryptoFailureAction(getConsumerCryptoFailureAction(spec.getConsumerCryptoFailureAction()))
                .producerCryptoFailureAction(getProducerCryptoFailureAction(spec.getProducerCryptoFailureAction()))
                .encryptionKeys(keyNames.toArray(new String[0]));

        return bldr.build();
    }

    public static CryptoKeyReader getCryptoKeyReaderInstance(String className, Map<String, Object> configs,
                                                             ClassLoader classLoader) {
        Class<?> cryptoClass;
        try {
            cryptoClass = ClassLoaderUtils.loadClass(className, classLoader);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(
                    String.format("Failed to load crypto key reader class %sx", className));
        }

        try {
            Constructor<?> ctor = cryptoClass.getConstructor(Map.class);
            return (CryptoKeyReader) ctor.newInstance(configs);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Key reader class does not have constructor accepts map", e);
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
            throw new RuntimeException("Failed to create instance for key reader class", e);
        }
    }

    public static ProducerCryptoFailureAction getProducerCryptoFailureAction(CryptoSpec.FailureAction action) {
        switch (action) {
            case FAIL:
                return ProducerCryptoFailureAction.FAIL;
            case SEND:

View on GitHub (pinned to 820761864e)

Solutions

  1. Correct the fully-qualified class name in the crypto config (also verify no trailing whitespace/newline)
  2. Add the jar containing the CryptoKeyReader implementation to the classpath (client -cp, connector's extra deps directory, or fat jar)
  3. Verify the class implements org.apache.pulsar.client.api.CryptoKeyReader and is public

Example fix

// before
Map<String,String> m = new HashMap<>();
m.put("cryptoKeyReaderClassName", "com.example.MyCryptoReader"); // class not in jar
// after
m.put("cryptoKeyReaderClassName", "com.example.crypto.MyCryptoKeyReader");
// and: add target/my-crypto-impl.jar to the connector's extra deps or client classpath
Defensive patterns

Strategy: try-catch

Validate before calling

try {
    Class.forName(cryptoKeyReaderClassName, true, classLoader);
} catch (ClassNotFoundException e) {
    throw new IllegalStateException("CryptoKeyReader class not on classpath: " + cryptoKeyReaderClassName);
}

Try / catch

try {
    CryptoKeyReader reader = CryptoUtils.getCryptoKeyReaderInstance(className, configs, classLoader);
} catch (RuntimeException e) {
    if (e.getMessage().startsWith("Failed to load crypto key reader class")) { /* fix class name / add jar */ }
}

Prevention

When it happens

Trigger: Producer/Consumer/Reader crypto config specifies cryptoKeyReaderClassName for a class not on the classpath of the client/connector, e.g. a typo in the fully-qualified name, or the jar containing the implementation was never added to the client's/worker's classpath.

Common situations: fat-jar for a Pulsar IO connector not including the custom CryptoKeyReader class; class name changed after refactoring/upgrade; running the client with a shaded classloader where the crypto class lives in a different jar.

Related errors


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