{"record":{"id":"c432e51e33c577b0","repo":"apache/pulsar","slug":"failed-to-acquire-tls-material-for-purpose-purpo","errorCode":null,"errorMessage":"Failed to acquire TLS material for purpose ${purpose}","messagePattern":"Failed to acquire TLS material for purpose (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"pulsar-broker-common/src/main/java/org/apache/pulsar/jetty/tls/JettyTlsFactory.java","lineNumber":472,"sourceCode":"     * so a configuration failure surfaces as its own cause. The underlying messages (\"No TLS material\n     * configured for server purpose WEB\") are the actionable ones and should not arrive wrapped.\n     *\n     * @param pending the acquisition\n     * @param purpose the purpose being acquired, for the failure message\n     * @return the acquisition result\n     */\n    private static <T> T awaitAcquisition(CompletableFuture<T> pending, TlsPurpose purpose) {\n        try {\n            return pending.join();\n        } catch (CompletionException e) {\n            Throwable cause = e.getCause() != null ? e.getCause() : e;\n            if (cause instanceof RuntimeException runtime) {\n                throw runtime;\n            }\n            if (cause instanceof Error error) {\n                throw error;\n            }\n            throw new IllegalStateException(\"Failed to acquire TLS material for purpose \" + purpose, cause);\n        }\n    }\n\n    /**\n     * Overlay a factory-supplied engine baseline onto a synthesized server factory (PIP-478 merge order):\n     * enabled protocols/cipher suites when the companion sets them, and the companion's client-auth mode as\n     * authoritative (rule 4) — {@code needClientAuth} wins over {@code wantClientAuth}, neither means none.\n     */\n    private static void applyServerBaseline(SslContextFactory.Server sslContextFactory, SSLParameters baseline) {\n        if (baseline.getProtocols() != null) {\n            sslContextFactory.setIncludeProtocols(baseline.getProtocols());\n        }\n        if (baseline.getCipherSuites() != null) {\n            sslContextFactory.setIncludeCipherSuites(baseline.getCipherSuites());\n        }\n        if (baseline.getNeedClientAuth()) {\n            sslContextFactory.setNeedClientAuth(true);\n        } else if (baseline.getWantClientAuth()) {","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-common/src/main/java/org/apache/pulsar/jetty/tls/JettyTlsFactory.java#L454-L490","documentation":"JettyTlsFactory.awaitAcquisition blocks until TLS key/certificate material (the native Jetty SslContextFactory) has been resolved for a given 'purpose'. If the asynchronous acquisition completes exceptionally, RuntimeExceptions/Errors are rethrown as-is; anything else is wrapped in this IllegalStateException. It means the broker could not obtain or build its TLS context (bad keystore, unreadable key file, failed decryption), so TLS listeners cannot start.","triggerScenarios":"Calling acquireNativeJettyFactory / subscription / resolveBaselineParameters when the underlying acquisition future failed with a non-RuntimeException cause: KeyStore load failure, UnrecoverableKeyException, FileNotFoundException on the keystore/truststore path, wrong key-store password, or unsupported certificate format.","commonSituations":"Misconfigured tlsCertificateFilePath/tlsKeyFilePath or keystore paths in broker.conf; wrong keystore password; expired/invalid certificates; file permissions preventing the broker user from reading the TLS material; switching TLS providers (PEM vs JKS/PKCS12) without matching config.","solutions":["Inspect the 'cause' of the IllegalStateException — it names the real TLS failure (bad password, missing file, invalid keystore).","Verify the keystore/certificate and key file paths exist and are readable by the broker process user.","Confirm the keystore password and type (JKS/PKCS12) match the actual file; for PEM, check tlsKeyFilePath/tlsCertificateFilePath/tlsTrustCertsFilePath values.","Validate the certificate/key pair and chain (openssl x509 / keytool -list) and renew if expired.","Ensure the TLS provider configuration (e.g. OpenSSL vs JDK provider) is consistent with the supplied material."],"exampleFix":"// before (broker.conf)\ntlsCertificateFilePath=/missing/cert.pem\ntlsKeyFilePath=/missing/key.pem\n// after\ntlsCertificateFilePath=/etc/pulsar/tls/broker.cert.pem\ntlsKeyFilePath=/etc/pulsar/tls/broker.key.pem\n# chmod 600 /etc/pulsar/tls/broker.key.pem","handlingStrategy":"validation","validationCode":"// Validate TLS material before broker startup\njava.nio.file.Path cert = java.nio.file.Path.of(conf.getTlsCertificateFilePath());\njava.nio.file.Path key = java.nio.file.Path.of(conf.getTlsKeyFilePath());\nif (!java.nio.file.Files.isReadable(cert)) throw new IllegalStateException(\"Unreadable cert: \" + cert);\nif (!java.nio.file.Files.isReadable(key)) throw new IllegalStateException(\"Unreadable key: \" + key);\nif (conf.getTlsKeyStorePassword() != null) {\n    try (var in = java.nio.file.Files.newInputStream(java.nio.file.Path.of(conf.getTlsKeyStore()))) {\n        var ks = java.security.KeyStore.getInstance(\"PKCS12\");\n        ks.load(in, conf.getTlsKeyStorePassword().toCharArray());\n    } catch (Exception e) {\n        throw new IllegalStateException(\"Keystore cannot be loaded: \" + e.getMessage(), e);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    factory.acquireNativeJettyFactory(purpose).get();\n} catch (java.util.concurrent.ExecutionException e) {\n    throw new IllegalStateException(\"TLS material invalid for purpose \" + purpose + \": \" + e.getCause(), e.getCause());\n} catch (InterruptedException e) {\n    Thread.currentThread().interrupt();\n}","preventionTips":["Verify cert/key paths and file permissions (readable by the broker user, key mode 600) before start.","Confirm keystore passwords and store types match the actual files.","Monitor certificate expiry and renew ahead of time.","Keep PEM/JKS provider config consistent; run keytool -list or openssl checks in deploy pipelines.","Always log the cause chain — the IllegalStateException wraps the real TLS error."],"tags":["tls","ssl","keystore","jetty","pulsar"],"backgroundTag":"tls-material-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"}