apache/pulsar · error · KeyStoreException

The pinned provider's TrustManagerFactory returned no X509Tr

Error message

The pinned provider's TrustManagerFactory returned no X509TrustManager; trust cannot be established. Managers: trustManagers

What it means

TlsContexts.singleX509TrustManager selects the X509TrustManager from the array produced by a TrustManagerFactory. If the pinned provider's factory yielded no X509TrustManager (empty array or only other TrustManager types), client trust cannot be established and KeyStoreException is thrown listing the managers found.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/tls/impl/TlsContexts.java:467

        TrustManager[] trustManagers = JdkSslContexts.createTrustManagers(material.trustCertsArray(), false,
                jsseProvider, jcaProvider);
        builder.trustManager(singleX509TrustManager(trustManagers));
    }

    /**
     * Netty's {@code trustManager(TrustManager)} accepts exactly one manager (it wraps it in a
     * {@code TrustManagerFactory}), and a {@code TrustManagerFactory} initialized from a keystore returns a
     * single {@code X509TrustManager}, so pick that one and fail loudly rather than silently dropping trust if
     * a provider ever returns something unexpected.
     */
    private static TrustManager singleX509TrustManager(TrustManager[] trustManagers)
            throws GeneralSecurityException {
        for (TrustManager trustManager : trustManagers) {
            if (trustManager instanceof X509TrustManager) {
                return trustManager;
            }
        }
        throw new KeyStoreException("The pinned provider's TrustManagerFactory returned no X509TrustManager; "
                + "trust cannot be established. Managers: " + Arrays.toString(trustManagers));
    }

    /**
     * Install the client identity on a Netty builder. Keystore material may hold several identities (e.g.
     * RSA + EC, or identities issued by different accepted CAs), so the whole set goes to a
     * {@link KeyManagerFactory} and JSSE selects one by the peer's requested key type / acceptable issuers.
     * PEM material has a single identity (key + chain) and keeps Netty's raw overload unless a provider axis
     * is pinned, in which case it too must go through a factory this class builds (see {@link #applyTrust}).
     */
    private static void applyKeyManager(SslContextBuilder builder, TlsMaterial material, TlsPolicy policy)
            throws Exception {
        if (material.hasKeyStoreEntries()) {
            builder.keyManager(buildKeyManagerFactory(material, policy));
        } else if (material.hasKeyMaterial()) {
            KeyManagerFactory pinned = pinnedPemKeyManagerFactory(material, policy);
            if (pinned != null) {
                builder.keyManager(pinned);

View on GitHub (pinned to 820761864e)

Solutions

  1. Switch to the default provider, or verify the pinned provider supports the configured truststore type and X.509 trust management
  2. Inspect the manager list printed in the message to see what the factory returned
  3. Check the truststore file is valid, non-empty, and loadable by the provider
  4. Upgrade or replace the provider; re-enable the default JVM TrustManagerFactory if the pinned one is unnecessary

Example fix

// before
builder.trustManager(customProviderTmf); // yields no X509TrustManager
// after
builder.trustManager(defaultTmf); // standard X509TrustManager
Defensive patterns

Strategy: type-guard

Type guard

static boolean hasX509TrustManager(TrustManagerFactory tmf) {
    for (TrustManager tm : tmf.getTrustManagers()) {
        if (tm instanceof X509TrustManager) return true;
    }
    return false;
}

Try / catch

try {
    TlsContexts.applyTrust(builder, tmf);
} catch (KeyStoreException e) {
    log.error("Pinned provider returned no X509TrustManager: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Building a TLS context with a pinned security provider whose TrustManagerFactory.getTrustManagers() returns no X509TrustManager — e.g. a provider that cannot handle the configured truststore type or yields only custom manager types.

Common situations: Non-default pinned security provider incompatible with the truststore type; misconfigured/empty truststore producing zero managers; provider or JDK upgrade changing manager types.

Related errors


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