apache/pulsar · error · IllegalArgumentException

privateKeyProvider must be set when failureAction is FAIL

Error message

privateKeyProvider must be set when failureAction is FAIL

What it means

ConsumerEncryptionPolicy enforces that a PrivateKeyProvider is supplied when the crypto failure action is ConsumerCryptoFailureAction.FAIL. With FAIL, the consumer must be able to decrypt messages and will fail consumption on any decryption error; without a key provider it could never decrypt anything, so the constructor throws IllegalArgumentException rather than allowing a configuration that always fails.

Source

Thrown at pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ConsumerEncryptionPolicy.java:48

 * <p>Construct via {@link #builder()}. The {@link PrivateKeyProvider} is required
 * when {@link #failureAction()} is {@link ConsumerCryptoFailureAction#FAIL} (the
 * default — strict mode); for {@link ConsumerCryptoFailureAction#DISCARD} or
 * {@link ConsumerCryptoFailureAction#CONSUME} the provider may be omitted, in
 * which case the consumer just relies on the failure action to decide what to do
 * with encrypted messages it can't decrypt.
 */
@EqualsAndHashCode
@ToString
public final class ConsumerEncryptionPolicy {

    private final PrivateKeyProvider privateKeyProvider;
    private final ConsumerCryptoFailureAction failureAction;

    private ConsumerEncryptionPolicy(PrivateKeyProvider privateKeyProvider,
                                     ConsumerCryptoFailureAction failureAction) {
        Objects.requireNonNull(failureAction, "failureAction must not be null");
        if (failureAction == ConsumerCryptoFailureAction.FAIL && privateKeyProvider == null) {
            throw new IllegalArgumentException(
                    "privateKeyProvider must be set when failureAction is FAIL");
        }
        this.privateKeyProvider = privateKeyProvider;
        this.failureAction = failureAction;
    }

    /**
     * @return the provider used to load private keys for decryption, or {@code null}
     *         when the consumer doesn't decrypt and falls back to the failure action
     *         (DISCARD or CONSUME)
     */
    public PrivateKeyProvider privateKeyProvider() {
        return privateKeyProvider;
    }

    /**
     * @return the action the consumer takes when decryption fails
     */

View on GitHub (pinned to 820761864e)

Solutions

  1. Provide a key provider: .privateKeyProvider(...) / .keyProvider(...) with a working implementation before building.
  2. If you don't have keys, use ConsumerCryptoFailureAction.CONSUME or DISCARD instead of FAIL.
  3. Ensure the key source (KMS credentials, key file path, env vars) is actually available at client startup.

Example fix

// before
ConsumerEncryptionPolicy p = ConsumerEncryptionPolicy.builder()
    .failureAction(ConsumerCryptoFailureAction.FAIL) // IllegalArgumentException: no key provider
    .build();

// after
ConsumerEncryptionPolicy p = ConsumerEncryptionPolicy.builder()
    .privateKeyProvider(new DefaultKeyProvider())
    .failureAction(ConsumerCryptoFailureAction.FAIL)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

if (action == ConsumerCryptoFailureAction.FAIL && keyProvider == null) {
    throw new IllegalStateException("A privateKeyProvider is required when failureAction is FAIL");
}
ConsumerEncryptionPolicy p = ConsumerEncryptionPolicy.builder()
    .privateKeyProvider(keyProvider)
    .failureAction(action)
    .build();

Type guard

static boolean isFailConfigValid(ConsumerCryptoFailureAction a, PrivateKeyProvider p) {
    return a != ConsumerCryptoFailureAction.FAIL || p != null;
}

Try / catch

try {
    policy = ConsumerEncryptionPolicy.builder()
        .failureAction(ConsumerCryptoFailureAction.FAIL)
        .privateKeyProvider(provider)
        .build();
} catch (IllegalArgumentException e) {
    log.error("FAIL action requires a key provider; check encryption config", e);
    throw e;
}

Prevention

When it happens

Trigger: Calling ConsumerEncryptionPolicy with failureAction = FAIL and privateKeyProvider = null, e.g. ConsumerEncryptionPolicy.builder().failureAction(ConsumerCryptoFailureAction.FAIL).build() without a keyProvider(...); or wiring FAIL dynamically from config where the provider was never configured.

Common situations: Tightening the failure action from CONSUME/DISCARD to FAIL for security without also adding the key provider; a key management service or env-based key config missing at startup; environment promotion (dev without encryption keys to prod with encrypted topics).

Related errors


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