apache/pulsar · error · java.lang.IllegalArgumentException
Cross-format TLS material: tlsPolicy(...) configures a keyst
Error message
Cross-format TLS material: tlsPolicy(...) configures a keystore truststore (trustStorePath) but the authentication plugin supplies a PEM client certificate/key. Folding these would silently drop the configured truststore. Configure the trust material and the client identity in the same format (both PEM, or both keystore).
What it means
At build() time the builder folds the authentication plugin's client certificate/key (PEM files) into the configured CLIENT_DEFAULT TlsPolicy. If that policy was configured with a keystore truststore (trustStorePath), the fold would drop the configured trust anchors — a PEM policy has no truststore field — so the library fails loudly with this IllegalArgumentException instead of silently falling back to the system trust store.
Source
Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/PulsarClientBuilderV5.java:496
private void mergeClientDefault(ClientConfigurationData target,
java.util.function.Function<TlsPolicy, TlsPolicy> merge) {
Map<TlsPurpose, TlsPolicy> map = target.getTlsPolicyMap();
TlsPolicy base = map.get(TlsPurpose.CLIENT_DEFAULT);
map.put(TlsPurpose.CLIENT_DEFAULT, merge.apply(base));
}
/** Copy the trust material and flags of {@code base} (if any) into a PEM-format builder. */
private static TlsPolicy.Builder pemBuilder(TlsPolicy base) {
TlsPolicy.Builder b = copyFlags(base).format(TlsPolicy.Format.PEM);
if (base != null && base.format() == TlsPolicy.Format.PEM) {
b.trustCertsFilePath(base.trustCertsFilePath());
} else if (base != null && isNotBlank(base.trustStorePath())) {
// Cross-format fold: the tlsPolicy(...) carries a keystore truststore but the auth plugin's client
// identity is PEM. A PEM policy has no truststore field, so folding here would silently drop the
// configured trust anchors and fall back to the system trust store. Fail loud (matching
// TlsPolicy.build()'s fail-loud format validation) rather than silently broadening/breaking trust.
throw new IllegalArgumentException("Cross-format TLS material: tlsPolicy(...) configures a keystore "
+ "truststore (trustStorePath) but the authentication plugin supplies a PEM client "
+ "certificate/key. Folding these would silently drop the configured truststore. Configure the "
+ "trust material and the client identity in the same format (both PEM, or both keystore).");
}
return b;
}
/** Copy the trust material and flags of {@code base} (if any) into a keystore-format builder. */
private static TlsPolicy.Builder keyStoreBuilder(TlsPolicy base) {
TlsPolicy.Builder b = copyFlags(base).format(TlsPolicy.Format.KEYSTORE);
if (base != null && base.format() == TlsPolicy.Format.KEYSTORE) {
// Preserve the base truststore (path, password, and TYPE): folding the auth plugin's keystore must
// not clobber the truststore type configured via tlsPolicy(...) — the keystore and truststore may
// use different types (e.g. a PKCS12 keystore with a JKS truststore).
b.trustStorePath(base.trustStorePath())
.trustStorePassword(base.trustStorePassword())
.trustStoreType(base.trustStoreType());
} else if (base != null && isNotBlank(base.trustCertsFilePath())) {View on GitHub (pinned to 820761864e)
Solutions
- Make trust and identity formats consistent: replace trustStorePath with trustCertsFilePath(...) (PEM CA bundle) on the tlsPolicy, matching the PEM client identity.
- Alternatively switch the auth plugin to a keystore-based one (AuthenticationKeyStoreTls) so identity is keystore-format like the truststore.
- Split trust domains if genuinely needed: configure trust via the keystore policy but supply the client identity through the same keystore policy rather than a PEM plugin.
- If the truststore was unintentional, remove trustStorePath from the policy so the fold completes without dropping trust material.
Example fix
// before
builder.tlsPolicy(TlsPolicy.builder().format(KEYSTORE).trustStorePath("truststore.jks").build())
.authentication(AuthenticationFactory.tls("cert.pem", "key.pem")); // IllegalArgumentException at build()
// after
builder.tlsPolicy(TlsPolicy.builder().format(PEM)
.trustCertsFilePath("ca-cert.pem").build())
.authentication(AuthenticationFactory.tls("cert.pem", "key.pem")); Defensive patterns
Strategy: validation
Validate before calling
// Before build(), when using a PEM auth plugin with a tlsPolicy:
TlsPolicy p = clientPolicy; // your configured CLIENT_DEFAULT policy
boolean pemPlugin = usesPemIdentity(authPlugin);
if (pemPlugin && p != null && p.format() == TlsPolicy.Format.KEYSTORE
&& p.trustStorePath() != null && !p.trustStorePath().isBlank()) {
throw new IllegalStateException("Use trustCertsFilePath (PEM) with a PEM auth plugin");
} Try / catch
try {
client = builder.build();
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Cross-format TLS material")) {
throw new IllegalStateException("Align trust + identity TLS formats (both PEM or both keystore)", e);
}
throw e;
} Prevention
- Standardize one TLS material format (PEM recommended) across truststore and client identity.
- If trust anchors arrive as JKS/PKCS12, convert to PEM (keytool -exportcert / openssl) for PEM-based setups.
- Keep a startup smoke test that builds the client so format mismatches surface at boot, not first connect.
- Document which format your ops team distributes.
When it happens
Trigger: Calling tlsPolicy(policy) with a KEYSTORE-format policy that sets trustStorePath, AND configuring a PEM-based auth plugin (AuthenticationTls with cert/key file paths, or a generic v4 plugin exposing PEM cert/key via getAuthData()), then calling build().
Common situations: Mixed TLS configuration where ops provided a JKS/PKCS12 truststore for broker verification but the app uses AuthenticationTls with PEM cert/key files; migrating a v4 client config where these were separate settings and never cross-checked; copying TLS settings from a keystore-based service into a PEM-based client setup.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Cross-format TLS material: tlsPolicy(...) configures a PEM t
- Failed to acquire TLS material for purpose ${purpose}
- TlsPolicy field '${field}' is set but is not valid for forma
- The replication cluster does not provide TLS encrypted servi
- No usable service URL (useTls=${useTls}, serviceUrl=${servic
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/ab9f3721331bc9eb.
Report an issue: GitHub.