apache/pulsar · error · PulsarClientException.UnsupportedAuthenticationException
v4 plugin ${v4.getClass().getName} returned no Authenticatio
Error message
v4 plugin ${v4.getClass().getName} returned no AuthenticationDataProvider What it means
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.
Source
Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/v5/LegacyV4AuthenticationAdapter.java:373
return Optional.of(kind.cast(this));
}
if (kind == HttpAuthHeadersProvider.class) {
return supportsHttp ? Optional.of(kind.cast(this)) : Optional.empty();
}
return Optional.empty();
}
@Override
public String authMethodName() {
return v4.getAuthMethodName();
}
@Override
public CompletableFuture<BinaryAuthData> getAuthDataAsync(AuthenticationCallContext callContext) {
return supplyOffloaded(() -> {
AuthenticationDataProvider d = v4.getAuthData(callContext.brokerHost());
if (d == null) {
throw new PulsarClientException.UnsupportedAuthenticationException(
"v4 plugin " + v4.getClass().getName() + " returned no AuthenticationDataProvider");
}
// The provider is retained for this exchange so a following challenge round continues the
// same conversation, and the initial credential comes from authenticate(INIT_AUTH_DATA) —
// verbatim what the v4 client did. Its default implementation returns getCommandData(), so a
// single-pass plugin is unaffected, while a challenge/response plugin (which serves its
// first frame only through authenticate) keeps working.
callContext.setStateObject(AuthenticationDataProvider.class, d);
AuthData initial = d.authenticate(AuthData.INIT_AUTH_DATA);
return new BinaryAuthData(initial == null ? new byte[0] : initial.getBytes());
});
}
@Override
public CompletableFuture<ChallengeResponse> respondToChallengeAsync(AuthenticationCallContext callContext,
AuthChallenge challenge) {
return supplyOffloaded(() -> {
AuthenticationDataProvider d = callContext.getStateObject(AuthenticationDataProvider.class)View on GitHub (pinned to 820761864e)
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.
Example fix
// before (in v4 plugin)
public AuthenticationDataProvider getAuthData(String host) { return configured ? data : null; }
// after
public AuthenticationDataProvider getAuthData(String host) {
if (!configured) throw new AuthenticationException("plugin not configured");
return data;
} Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: exercise the plugin before the handshake
AuthenticationDataProvider d = v4.getAuthData(expectedBrokerHost);
if (d == null) {
throw new IllegalStateException("v4 plugin " + v4.getClass().getName() + " returned null for host " + expectedBrokerHost);
} Try / catch
try {
BinaryAuthData data = adapter.getAuthDataAsync(ctx).join();
} catch (java.util.concurrent.CompletionException e) {
if (e.getCause() instanceof PulsarClientException.UnsupportedAuthenticationException
&& e.getCause().getMessage().contains("returned no AuthenticationDataProvider")) {
// reconfigure or replace the plugin
} else throw e.getCause();
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- v4 plugin ${v4.getClass().getName} provided no HTTP auth hea
- authentication plugin does not expose BinaryAuthDataProvide
- Invalid broker configuration. Authentication must be enabled
- Invalid combination of Original principal cannot be empty if
- Need to authenticate to perform the request
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/f31319f1f1dd9a53.
Report an issue: GitHub.