{"record":{"id":"4eafb91615441fa9","repo":"apache/pulsar","slug":"failed-to-obtain-broker-client-authentication-tls","errorCode":null,"errorMessage":"Failed to obtain broker-client authentication TLS material","messagePattern":"Failed to obtain broker-client authentication TLS material","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/tls/impl/FileBasedTlsFactory.java","lineNumber":155,"sourceCode":"        this.registry = Map.copyOf(built);\n    }\n\n    /**\n     * Adapt a component's broker-client {@link Authentication} to a per-refresh\n     * {@link AuthenticationDataProvider} supplier for use with the {@code BROKER_CLIENT} fold constructor.\n     * The supplier re-reads {@code getAuthData()} on each poll so credential rotation is observed; a checked\n     * {@link PulsarClientException} is rethrown unchecked and handled by the factory's keep-last-good poll.\n     *\n     * @param authentication the broker-client authentication plugin (never {@code null})\n     * @return a supplier of the plugin's current authentication data\n     */\n    public static Supplier<AuthenticationDataProvider> authMaterialSupplier(Authentication authentication) {\n        Objects.requireNonNull(authentication, \"authentication must not be null\");\n        return () -> {\n            try {\n                return resolveAuthData(authentication);\n            } catch (PulsarClientException e) {\n                throw new RuntimeException(\"Failed to obtain broker-client authentication TLS material\", e);\n            }\n        };\n    }\n\n    // The BROKER_CLIENT TLS fold is host-agnostic — TLS key material does not vary by peer — so the\n    // host-less getAuthData() is exactly what we want; isolate its deprecation here.\n    @SuppressWarnings(\"deprecation\")\n    private static AuthenticationDataProvider resolveAuthData(Authentication authentication)\n            throws PulsarClientException {\n        return authentication.getAuthData();\n    }\n\n    @Override\n    public CompletableFuture<Void> initialize(TlsFactoryInitContext context) {\n        try {\n            Objects.requireNonNull(context, \"context must not be null\");\n            // Required, not optional: every acquisition path in this factory reads files and parses key\n            // material. Without an executor the work would run inline on the caller's thread, which the SPI","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/tls/impl/FileBasedTlsFactory.java#L137-L173","documentation":"FileBasedTlsFactory.authMaterialSupplier returns a Supplier of AuthenticationDataProvider for broker-client TLS. Any PulsarClientException thrown while resolving the client authentication data (e.g. via getAuthData) is rethrown as a RuntimeException with this message, preserving the original as the cause.","triggerScenarios":"Invoking the supplier returned by authMaterialSupplier(authentication) when the configured Authentication implementation's getAuthData() throws PulsarClientException, e.g. because referenced certificate/key/credential files are missing or unreadable.","commonSituations":"Broker-client auth config pointing to cert/key paths that do not exist or are unreadable by the process user; Authentication provider not initialized before the supplier is used; invalid auth state after config reload.","solutions":["Inspect the chained cause (e.getCause(), a PulsarClientException) for the actual failure","Verify all cert/key/credential files referenced by the Authentication config exist and are readable","Ensure the Authentication object was fully initialized before the supplier is invoked","Fix the underlying PulsarClientException condition rather than catching the wrapper"],"exampleFix":"// before\nAuthenticationTls auth = new AuthenticationTls(); // cert paths unset\nFileBasedTlsFactory.authMaterialSupplier(auth).get(); // throws RuntimeException\n// after\nAuthenticationTls auth = new AuthenticationTls();\nauth.configure(Map.of(\"tlsCertFile\", \"/etc/pulsar/cert.pem\", \"tlsKeyFile\", \"/etc/pulsar/key.pem\"));\nFileBasedTlsFactory.authMaterialSupplier(auth).get();","handlingStrategy":"try-catch","validationCode":"// before invoking the supplier, check configured material paths exist:\nfor (String p : List.of(certPath, keyPath)) {\n    if (p != null && !Files.isReadable(Path.of(p))) throw new IllegalStateException(\"Missing auth material: \" + p);\n}","typeGuard":null,"tryCatchPattern":"try {\n    AuthenticationDataProvider data = authMaterialSupplier(authentication).get();\n} catch (RuntimeException e) {\n    Throwable cause = e.getCause(); // PulsarClientException with the real reason\n    log.error(\"Broker-client TLS auth material unavailable: {}\", cause == null ? e : cause, cause);\n}","preventionTips":["Verify the Authentication implementation is initialized and its cert/key paths exist before calling the supplier","Always log the chained cause — the RuntimeException is only a wrapper","Fail fast at startup by eagerly invoking the supplier once"],"tags":["tls","authentication","supplier","wrapped-exception"],"backgroundTag":"tls-auth-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"}