{"record":{"id":"f31319f1f1dd9a53","repo":"apache/pulsar","slug":"v4-plugin-v4-getclass-getname-returned-no-aut","errorCode":null,"errorMessage":"v4 plugin ${v4.getClass().getName} returned no AuthenticationDataProvider","messagePattern":"v4 plugin (.+?) returned no AuthenticationDataProvider","errorType":"exception","errorClass":"PulsarClientException.UnsupportedAuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/v5/LegacyV4AuthenticationAdapter.java","lineNumber":373,"sourceCode":"                return Optional.of(kind.cast(this));\n            }\n            if (kind == HttpAuthHeadersProvider.class) {\n                return supportsHttp ? Optional.of(kind.cast(this)) : Optional.empty();\n            }\n            return Optional.empty();\n        }\n\n        @Override\n        public String authMethodName() {\n            return v4.getAuthMethodName();\n        }\n\n        @Override\n        public CompletableFuture<BinaryAuthData> getAuthDataAsync(AuthenticationCallContext callContext) {\n            return supplyOffloaded(() -> {\n                AuthenticationDataProvider d = v4.getAuthData(callContext.brokerHost());\n                if (d == null) {\n                    throw new PulsarClientException.UnsupportedAuthenticationException(\n                            \"v4 plugin \" + v4.getClass().getName() + \" returned no AuthenticationDataProvider\");\n                }\n                // The provider is retained for this exchange so a following challenge round continues the\n                // same conversation, and the initial credential comes from authenticate(INIT_AUTH_DATA) —\n                // verbatim what the v4 client did. Its default implementation returns getCommandData(), so a\n                // single-pass plugin is unaffected, while a challenge/response plugin (which serves its\n                // first frame only through authenticate) keeps working.\n                callContext.setStateObject(AuthenticationDataProvider.class, d);\n                AuthData initial = d.authenticate(AuthData.INIT_AUTH_DATA);\n                return new BinaryAuthData(initial == null ? new byte[0] : initial.getBytes());\n            });\n        }\n\n        @Override\n        public CompletableFuture<ChallengeResponse> respondToChallengeAsync(AuthenticationCallContext callContext,\n                AuthChallenge challenge) {\n            return supplyOffloaded(() -> {\n                AuthenticationDataProvider d = callContext.getStateObject(AuthenticationDataProvider.class)","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/v5/LegacyV4AuthenticationAdapter.java#L355-L391","documentation":"During the v5 authentication handshake, the v4 adapter calls the legacy plugin's getAuthData(brokerHost) off the event loop. If the plugin returns null — meaning it produced no AuthenticationDataProvider for this exchange — the adapter raises PulsarClientException.UnsupportedAuthenticationException naming the plugin class, because the protocol requires a provider to continue the conversation.","triggerScenarios":"A wrapped v4 plugin whose getAuthData(String) returns null during getAuthDataAsync — e.g. a plugin that only supports specific host names or returns null when its state was not initialized.","commonSituations":"Misconfigured v4 plugins (missing/malformed authParams so initialization silently failed); plugins written for a different broker hostname pattern; plugin implementations that return null instead of throwing when credentials are absent.","solutions":["Fix or reconfigure the v4 plugin (authParams/authParamsString) so getAuthData returns a valid AuthenticationDataProvider.","Check the broker hostname passed in the call context matches what the plugin expects (host-based plugins return null for unknown hosts).","If you own the plugin, return a provider or throw a descriptive AuthenticationException instead of returning null.","Verify the plugin is fully initialized (initialize(authParams) called) before the handshake."],"exampleFix":"// before (in v4 plugin)\npublic AuthenticationDataProvider getAuthData(String host) { return configured ? data : null; }\n// after\npublic AuthenticationDataProvider getAuthData(String host) {\n    if (!configured) throw new AuthenticationException(\"plugin not configured\");\n    return data;\n}","handlingStrategy":"validation","validationCode":"// pre-flight: exercise the plugin before the handshake\nAuthenticationDataProvider d = v4.getAuthData(expectedBrokerHost);\nif (d == null) {\n    throw new IllegalStateException(\"v4 plugin \" + v4.getClass().getName() + \" returned null for host \" + expectedBrokerHost);\n}","typeGuard":null,"tryCatchPattern":"try {\n    BinaryAuthData data = adapter.getAuthDataAsync(ctx).join();\n} catch (java.util.concurrent.CompletionException e) {\n    if (e.getCause() instanceof PulsarClientException.UnsupportedAuthenticationException\n            && e.getCause().getMessage().contains(\"returned no AuthenticationDataProvider\")) {\n        // reconfigure or replace the plugin\n    } else throw e.getCause();\n}","preventionTips":["Test the v4 plugin's getAuthData against the exact broker hostname used in production.","Never return null from a v4 plugin's getAuthData; throw a descriptive exception instead.","Ensure authParams are complete so plugin initialization succeeds before handshakes."],"tags":["authentication","unsupported-authentication","v4-plugin","handshake"],"backgroundTag":"authentication-provider-missing","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"}