{"record":{"id":"ebbe7c6d4c2b3071","repo":"apache/pulsar","slug":"tlsfactory-must-not-be-null","errorCode":null,"errorMessage":"tlsFactory must not be null","messagePattern":"tlsFactory must not be null","errorType":"exception","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/PulsarClientBuilderV5.java","lineNumber":429,"sourceCode":"        // being the only v5 expression of the legacy client.conf useTls=true with a plain pulsar:// URL.\n        // BROKER_CLIENT and plugin-minted purposes (TlsPurpose.client(\"...\")) are client-role too, and\n        // enabling the transport for them is the same defect this guard was added to fix.\n        if (TlsPurpose.CLIENT_DEFAULT.equals(purpose)) {\n            conf.setUseTls(true);\n        }\n        Map<TlsPurpose, TlsPolicy> map = conf.getTlsPolicyMap();\n        if (map == null) {\n            map = new LinkedHashMap<>();\n            conf.setTlsPolicyMap(map);\n        }\n        map.put(purpose, policy);\n        return this;\n    }\n\n    @Override\n    public PulsarClientBuilder tlsFactory(PulsarTlsFactory factory) {\n        if (factory == null) {\n            throw new IllegalArgumentException(\"tlsFactory must not be null\");\n        }\n        // Same rule as tlsPolicy above, and pip-478.md states it for this method by name: a factory supplies\n        // material for every purpose WITHOUT enabling transport TLS. Forcing useTls here made an adopted\n        // factory on a plaintext pulsar:// URL — the CLIENT_OAUTH2-only case the SPI exists to serve —\n        // attempt a TLS handshake against the plaintext broker port. The adopted factory is still composed\n        // and initialized: PulsarClientImpl.needsClientTlsFactory() has its own arm for conf.getTlsFactory().\n        conf.setTlsFactory(factory);\n        return this;\n    }\n\n    /**\n     * Fold a bridged third-party v4 plugin's file-based TLS material into {@link TlsPurpose#CLIENT_DEFAULT}\n     * (PIP-478). The plugin's {@code getAuthData()} has already been probed by\n     * {@link #resolveGenericV4} on the application thread (off the event loop), and {@code data} is known to\n     * report {@code hasDataForTls()}. Only <em>file-based</em> material (PEM cert/key file paths or a\n     * keystore) can be represented in the file-path {@link TlsPolicy}; a plugin that exposes only in-memory\n     * cert/key material is logged rather than silently dropped, since it cannot be folded on this path.\n     *","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/PulsarClientBuilderV5.java#L411-L447","documentation":"tlsFactory(PulsarTlsFactory) throws IllegalArgumentException when the factory argument is null. A null factory cannot be distinguished from 'not configured' once stored, and there is no other way to clear the slot, so the builder rejects null outright rather than silently unconfiguring TLS.","triggerScenarios":"Calling clientBuilder.tlsFactory(null) directly, or tlsFactory(someFactory) where a lookup/provider returned null (DI container without binding, config-driven factory creation that failed silently).","commonSituations":"Conditional factory construction like config.getBoolean(\"tls\") ? buildFactory() : null; Spring/Guice optional bindings resolving to null; refactoring that changed a factory provider's failure mode from throwing to returning null.","solutions":["Construct a valid PulsarTlsFactory (e.g. via its builder) before calling tlsFactory.","Only call tlsFactory when a factory actually exists; otherwise omit the call — omitting is the correct way to have no factory.","Fix the factory provider so it throws on failure instead of returning null."],"exampleFix":"// before\nbuilder.tlsFactory(provider.get()); // provider.get() returns null -> IllegalArgumentException\n// after\nPulsarTlsFactory f = provider.get();\nif (f != null) {\n    builder.tlsFactory(f);\n}","handlingStrategy":"validation","validationCode":"PulsarTlsFactory f = factoryProvider.get();\nif (f != null) {\n    builder.tlsFactory(f);\n}","typeGuard":"static boolean hasFactory(java.util.function.Supplier<PulsarTlsFactory> s) { return s != null && s.get() != null; }","tryCatchPattern":null,"preventionTips":["Make factory providers throw instead of returning null on failure.","Omit the tlsFactory call entirely when no factory is configured — that is the correct 'unset'.","Construct the factory where you configure the builder so a construction failure is visible immediately."],"tags":["pulsar","tls","null-check","illegal-argument","builder"],"backgroundTag":"null-argument-validation","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}