{"record":{"id":"b1dbc223abd7add2","repo":"apache/pulsar","slug":"client-tls-factory-factory-getclass-getname","errorCode":null,"errorMessage":"Client TLS factory <factory.getClass().getName()> supplied no Netty SslContext for purpose <purpose>","messagePattern":"Client TLS factory <factory\\.getClass\\(\\)\\.getName\\(\\)> supplied no Netty SslContext for purpose <purpose>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/tls/ClientTlsFactorySupport.java","lineNumber":620,"sourceCode":"                .build();\n    }\n\n    /**\n     * Fail-fast probe: build one instance of the purpose and dispose it, surfacing a configuration error\n     * (e.g. a missing cert file) as an actionable {@link IllegalArgumentException}. Acquisition goes through\n     * {@link TlsContextAcquisition}, so a custom factory that supplies only the JDK {@code SSLContext}\n     * fallback probes successfully via the framework-synthesized Netty context.\n     *\n     * @param factory   the initialized factory\n     * @param purpose   the purpose to probe\n     * @param synthesis the settings baked into a synthesized Netty context on the fallback path\n     */\n    public static void probe(PulsarTlsFactory factory, TlsPurpose purpose, TlsSynthesisSpec synthesis) {\n        try {\n            Optional<TlsHandle<SslContext>> handle =\n                    TlsContextAcquisition.acquireNettyContext(factory, purpose, synthesis).get();\n            if (handle.isEmpty()) {\n                throw new IllegalStateException(\"Client TLS factory \" + factory.getClass().getName()\n                        + \" supplied no Netty SslContext for purpose \" + purpose);\n            }\n            handle.get().dispose();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new IllegalStateException(\"Interrupted while probing the client TLS factory\", e);\n        } catch (ExecutionException | CompletionException e) {\n            Throwable cause = e.getCause() != null ? e.getCause() : e;\n            throw new IllegalArgumentException(\"Client TLS configuration is invalid for purpose \" + purpose\n                    + \": \" + cause.getMessage(), cause);\n        }\n    }\n\n    private static void initializeBlocking(PulsarTlsFactory factory, TlsFactoryInitContext context)\n            throws Exception {\n        try {\n            factory.initialize(context).get();\n        } catch (ExecutionException e) {","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/tls/ClientTlsFactorySupport.java#L602-L638","documentation":"probe() validates a PulsarTlsFactory by asking it to produce a Netty SslContext for a given TlsPurpose (client/server connection) via TlsContextAcquisition. If the future completes with an empty Optional, the factory failed to supply a context for that purpose, and an IllegalStateException is thrown naming the factory class and purpose. This signals the factory implementation does not support or did not configure that purpose.","triggerScenarios":"resolveClientTlsFactory probing a custom PulsarTlsFactory whose acquireNettyContext returns empty for a purpose (e.g. factory only implements client auth context but probe requests server-purpose synthesis, or keys/certs missing so the factory yields no context).","commonSituations":"Custom TLS factory that returns Optional.empty for unsupported purposes; missing key/cert material so context creation is skipped; purpose/purpose-specific synthesis spec mismatch after a Pulsar upgrade adding new purposes.","solutions":["Implement the missing purpose in your PulsarTlsFactory so acquireNettyContext returns a valid SslContext handle.","Supply the required key/cert/truststore config so the factory can synthesize the context.","Check TlsContextAcquisition logs for why the acquisition completed empty (async failure swallowed).","Update the custom factory for the current Pulsar TlsPurpose enum / synthesis spec API."],"exampleFix":"// before (custom factory)\nOptional<TlsHandle<SslContext>> h = purpose == TlsPurpose.CLIENT ? build() : Optional.empty();\n// after\nOptional<TlsHandle<SslContext>> h = buildFor(purpose, synthesis); // build for every purpose","handlingStrategy":"validation","validationCode":"boolean supportsPurpose(PulsarTlsFactory f, TlsPurpose p) {\n    try {\n        var h = TlsContextAcquisition.acquireNettyContext(f, p, spec).get(5, TimeUnit.SECONDS);\n        boolean ok = h.isPresent(); h.ifPresent(TlsHandle::dispose);\n        return ok;\n    } catch (Exception e) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n    ClientTlsFactorySupport.probe(factory, purpose, spec);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"supplied no Netty SslContext\")) {\n        log.error(\"Factory lacks support/keys for purpose \" + purpose, e);\n    }\n}","preventionTips":["Implement all TlsPurpose cases in custom factories","Load key/cert material before registration so context creation cannot be skipped","Add a startup smoke test that probes each purpose","Log inside acquireNettyContext to surface swallowed async failures"],"tags":["tls","sslcontext","netty","factory"],"backgroundTag":"tls-context-unavailable","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"}