apache/pulsar · error · UnsupportedOperationException

getPublicKey called on a consumer-side CryptoKeyReaderAdapte

Error message

getPublicKey called on a consumer-side CryptoKeyReaderAdapter

What it means

CryptoKeyReaderAdapter wraps a v5 key provider for the legacy CryptoKeyReader interface. An adapter built only with a publicKeyProvider is producer-side; calling getPublicKey on a consumer-side adapter (built with only a privateKeyProvider) throws UnsupportedOperationException because there is no public key provider to delegate to.

Source

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

    /**
     * Producer-side adapter: only {@link CryptoKeyReader#getPublicKey} is supported.
     */
    static CryptoKeyReader forProducer(PublicKeyProvider provider) {
        return new CryptoKeyReaderAdapter(provider, null);
    }

    /**
     * Consumer-side adapter: only {@link CryptoKeyReader#getPrivateKey} is supported.
     */
    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 the producer-side factory / a non-null publicKeyProvider before using it for message encryption.
  2. Use separate adapter instances for producer (public keys) and consumer (private keys).
  3. Check the factory method used — forConsumer vs forProducer — matches the client role.

Example fix

// before
CryptoKeyReader reader = CryptoKeyReaderAdapter.forConsumer(privateKeyProvider);
Producer<byte[]> p = client.newProducer().cryptoKeyReader(reader); // producer needs public keys
// after
CryptoKeyReader reader = CryptoKeyReaderAdapter.forProducer(publicKeyProvider);
Producer<byte[]> p = client.newProducer().cryptoKeyReader(reader);
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the adapter was built for producers before wiring it into a Producer
CryptoKeyReaderAdapter adapter = CryptoKeyReaderAdapter.forProducer(publicKeyProvider);
producerBuilder.cryptoKeyReader(adapter);

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Configuring a producer's encryption with an adapter created from a consumer-side key provider (privateKeyProvider == null), then the producer (or client code) invokes getPublicKey.

Common situations: Copy-pasting consumer crypto configuration into a producer; sharing one adapter instance between a producer and consumer; a library defaulting to the consumer-side factory method CryptoKeyReaderAdapter.forConsumer(...) in producer setup.

Related errors


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