apache/pulsar · error · UnsupportedOperationException

getPrivateKey called on a producer-side CryptoKeyReaderAdapt

Error message

getPrivateKey called on a producer-side CryptoKeyReaderAdapter

What it means

This is the mirror of the producer-side case: CryptoKeyReaderAdapter built only with a privateKeyProvider is consumer-side. Calling getPrivateKey (needed for decryption on the consumer) on a producer-side adapter (privateKeyProvider == null) throws UnsupportedOperationException.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/CryptoKeyReaderAdapter.java:77

     */
    static CryptoKeyReader forConsumer(PrivateKeyProvider provider) {
        return new CryptoKeyReaderAdapter(null, provider);
    }

    @Override
    public EncryptionKeyInfo getPublicKey(String keyName, Map<String, String> metadata) {
        if (publicKeyProvider == null) {
            throw new UnsupportedOperationException(
                    "getPublicKey called on a consumer-side CryptoKeyReaderAdapter");
        }
        var v5Key = publicKeyProvider.getPublicKey(keyName).join();
        return new EncryptionKeyInfo(v5Key.key(), v5Key.metadata());
    }

    @Override
    public EncryptionKeyInfo getPrivateKey(String keyName, Map<String, String> metadata) {
        if (privateKeyProvider == null) {
            throw new UnsupportedOperationException(
                    "getPrivateKey called on a producer-side CryptoKeyReaderAdapter");
        }
        var v5Key = privateKeyProvider.getPrivateKey(keyName, metadata).join();
        return new EncryptionKeyInfo(v5Key.key(), v5Key.metadata());
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Build the adapter with a non-null privateKeyProvider (consumer-side factory) for consumer decryption.
  2. Give the consumer its own adapter instance with the private key provider.
  3. Verify environment/keystore actually exposes the private keys the consumer needs.

Example fix

// before
CryptoKeyReader reader = CryptoKeyReaderAdapter.forProducer(publicKeyProvider);
Consumer<byte[]> c = client.newConsumer().cryptoKeyReader(reader); // consumer needs private keys
// after
CryptoKeyReader reader = CryptoKeyReaderAdapter.forConsumer(privateKeyProvider);
Consumer<byte[]> c = client.newConsumer().cryptoKeyReader(reader);
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the adapter was built for consumers before wiring it into a Consumer
CryptoKeyReaderAdapter adapter = CryptoKeyReaderAdapter.forConsumer(privateKeyProvider);
consumerBuilder.cryptoKeyReader(adapter);

Type guard

static boolean supportsPrivateKeys(CryptoKeyReaderAdapter a) {
    return a != null && a.isConsumerSide(); // or track the factory used at construction
}

Try / catch

try {
    EncryptionKeyInfo ki = adapter.getPrivateKey(keyName, metadata);
} catch (UnsupportedOperationException e) {
    throw new IllegalStateException("Consumer configured with producer-side key reader", e);
}

Prevention

When it happens

Trigger: Configuring a consumer's decryption with an adapter created from a producer-side key provider (publicKeyProvider only), then the consumer invokes getPrivateKey during message decryption.

Common situations: Reusing the producer's CryptoKeyReader for a consumer; defaulting configuration code to the producer factory; one adapter shared across a client used for both producing and consuming.

Related errors


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