{"record":{"id":"285aad6919f96a0b","repo":"apache/pulsar","slug":"the-pinned-provider-s-trustmanagerfactory-returned","errorCode":null,"errorMessage":"The pinned provider's TrustManagerFactory returned no X509TrustManager; trust cannot be established. Managers: trustManagers","messagePattern":"The pinned provider's TrustManagerFactory returned no X509TrustManager; trust cannot be established\\. Managers: trustManagers","errorType":"exception","errorClass":"KeyStoreException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/tls/impl/TlsContexts.java","lineNumber":467,"sourceCode":"        TrustManager[] trustManagers = JdkSslContexts.createTrustManagers(material.trustCertsArray(), false,\n                jsseProvider, jcaProvider);\n        builder.trustManager(singleX509TrustManager(trustManagers));\n    }\n\n    /**\n     * Netty's {@code trustManager(TrustManager)} accepts exactly one manager (it wraps it in a\n     * {@code TrustManagerFactory}), and a {@code TrustManagerFactory} initialized from a keystore returns a\n     * single {@code X509TrustManager}, so pick that one and fail loudly rather than silently dropping trust if\n     * a provider ever returns something unexpected.\n     */\n    private static TrustManager singleX509TrustManager(TrustManager[] trustManagers)\n            throws GeneralSecurityException {\n        for (TrustManager trustManager : trustManagers) {\n            if (trustManager instanceof X509TrustManager) {\n                return trustManager;\n            }\n        }\n        throw new KeyStoreException(\"The pinned provider's TrustManagerFactory returned no X509TrustManager; \"\n                + \"trust cannot be established. Managers: \" + Arrays.toString(trustManagers));\n    }\n\n    /**\n     * Install the client identity on a Netty builder. Keystore material may hold several identities (e.g.\n     * RSA + EC, or identities issued by different accepted CAs), so the whole set goes to a\n     * {@link KeyManagerFactory} and JSSE selects one by the peer's requested key type / acceptable issuers.\n     * PEM material has a single identity (key + chain) and keeps Netty's raw overload unless a provider axis\n     * is pinned, in which case it too must go through a factory this class builds (see {@link #applyTrust}).\n     */\n    private static void applyKeyManager(SslContextBuilder builder, TlsMaterial material, TlsPolicy policy)\n            throws Exception {\n        if (material.hasKeyStoreEntries()) {\n            builder.keyManager(buildKeyManagerFactory(material, policy));\n        } else if (material.hasKeyMaterial()) {\n            KeyManagerFactory pinned = pinnedPemKeyManagerFactory(material, policy);\n            if (pinned != null) {\n                builder.keyManager(pinned);","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/tls/impl/TlsContexts.java#L449-L485","documentation":"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.","triggerScenarios":"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.","commonSituations":"Non-default pinned security provider incompatible with the truststore type; misconfigured/empty truststore producing zero managers; provider or JDK upgrade changing manager types.","solutions":["Switch to the default provider, or verify the pinned provider supports the configured truststore type and X.509 trust management","Inspect the manager list printed in the message to see what the factory returned","Check the truststore file is valid, non-empty, and loadable by the provider","Upgrade or replace the provider; re-enable the default JVM TrustManagerFactory if the pinned one is unnecessary"],"exampleFix":"// before\nbuilder.trustManager(customProviderTmf); // yields no X509TrustManager\n// after\nbuilder.trustManager(defaultTmf); // standard X509TrustManager","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean hasX509TrustManager(TrustManagerFactory tmf) {\n    for (TrustManager tm : tmf.getTrustManagers()) {\n        if (tm instanceof X509TrustManager) return true;\n    }\n    return false;\n}","tryCatchPattern":"try {\n    TlsContexts.applyTrust(builder, tmf);\n} catch (KeyStoreException e) {\n    log.error(\"Pinned provider returned no X509TrustManager: {}\", e.getMessage());\n}","preventionTips":["Verify the pinned provider supports the configured truststore type before adopting it","Pre-flight check tmf.getTrustManagers() for an X509TrustManager when using custom providers","Keep provider versions tested against the JDK in use"],"tags":["tls","truststore","security-provider","keystore"],"backgroundTag":"no-x509-trust-manager","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}