{"record":{"id":"ae04ce53009a78ac","repo":"apache/pulsar","slug":"v4-authentication-must-not-be-null","errorCode":null,"errorMessage":"v4 authentication must not be null","messagePattern":"v4 authentication must not be null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/v5/LegacyV4AuthenticationAdapter.java","lineNumber":143,"sourceCode":"     * {@code close} the caller will perform.\n     *\n     * <p>This is the client's own path. The client keeps the configured v4 plugin in its configuration and\n     * starts it during construction — the v4 instance remains observable, and several client-side features\n     * (notably folding an auth plugin's TLS material into the client's TLS policy) read it directly. The\n     * adapter that lets the client <em>drive</em> that plugin through the v5 model must therefore not run\n     * the lifecycle a second time: {@code AuthenticationOAuth2.start()} initializes the token flow, so a\n     * second {@code start()} is not a harmless no-op.\n     *\n     * @param v4 the already-configured, already-started v4 authentication plugin\n     * @return a v5 {@link Authentication} that delegates to the v4 plugin without re-running its lifecycle\n     */\n    public static Authentication wrapAlreadyStarted(org.apache.pulsar.client.api.Authentication v4) {\n        return wrap(v4, false);\n    }\n\n    private static Authentication wrap(org.apache.pulsar.client.api.Authentication v4, boolean ownsLifecycle) {\n        if (v4 == null) {\n            throw new IllegalArgumentException(\"v4 authentication must not be null\");\n        }\n        if (v4 instanceof AuthenticationDisabled) {\n            return NoAuthentication.INSTANCE;\n        }\n        String methodName = v4.getAuthMethodName();\n        if (TlsAuthentication.DEFAULT_AUTH_METHOD_NAME.equalsIgnoreCase(methodName)) {\n            return new LegacyV4TlsAdapter(v4, ownsLifecycle);\n        }\n        if (\"sasl\".equalsIgnoreCase(methodName)) {\n            return new LegacyV4ChallengeResponseAdapter(v4, ownsLifecycle);\n        }\n        return new LegacyV4CredentialAdapter(v4, ownsLifecycle);\n    }\n\n    /**\n     * Unwrap the v4 {@link org.apache.pulsar.client.api.Authentication} that a {@link #wrap}-produced\n     * v5 adapter delegates to, if any. This is the inverse of {@link #wrap} — it recovers the wrapped v4\n     * plugin for any bridged adapter (used by the v5 client builder to inspect a bridged plugin's TLS","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/v5/LegacyV4AuthenticationAdapter.java#L125-L161","documentation":"LegacyV4AuthenticationAdapter wraps an old (v4) org.apache.pulsar.client.api.Authentication plugin so it can be used with the v5 authentication framework. wrap() (private, used by both wrap and wrapAlreadyStarted) rejects a null v4 plugin immediately with this IllegalArgumentException, since there is nothing to adapt.","triggerScenarios":"Calling LegacyV4AuthenticationAdapter.wrap(v4) or wrapAlreadyStarted(v4) with a null argument, typically because authentication resolution returned null before wrapping.","commonSituations":"Client configuration code that builds the v4 Authentication instance conditionally and passes the null result into the adapter; refactors where a lookup map or registry returns null for an unknown auth class name.","solutions":["Pass a non-null v4 Authentication instance; if auth is intentionally disabled, pass AuthenticationDisabled (it maps to NoAuthentication.INSTANCE).","Null-check the v4 plugin before calling the adapter and fall back to AuthenticationDisabled when no plugin is configured.","Fix the upstream factory/registry so it never returns null for a configured auth plugin."],"exampleFix":"// before\nAuthentication v5 = LegacyV4AuthenticationAdapter.wrap(conf.getAuthentication()); // may be null\n// after\norg.apache.pulsar.client.api.Authentication v4 = conf.getAuthentication();\nAuthentication v5 = v4 == null\n        ? NoAuthentication.INSTANCE\n        : LegacyV4AuthenticationAdapter.wrap(v4);","handlingStrategy":"validation","validationCode":"if (v4 == null) {\n    v4 = new AuthenticationDisabled(); // or skip adapter entirely\n}","typeGuard":"static boolean isWrappable(org.apache.pulsar.client.api.Authentication a) {\n    return a != null;\n}","tryCatchPattern":"try {\n    v5 = LegacyV4AuthenticationAdapter.wrap(v4);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"must not be null\")) {\n        v5 = NoAuthentication.INSTANCE;\n    } else throw e;\n}","preventionTips":["Resolve the v4 plugin through a single factory that returns AuthenticationDisabled rather than null.","Null-check plugin instances immediately after config parsing.","Prefer v5-native plugins so the legacy adapter path is not used at all."],"tags":["authentication","null-check","illegal-argument","v4-adapter"],"backgroundTag":"null-authentication-plugin","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"}