apache/pulsar · warning · UnsupportedCallbackException
Unrecognized SASL GSSAPI Client Callback.
Error message
Unrecognized SASL GSSAPI Client Callback.
What it means
PulsarSaslClient's ClientCallbackHandler only understands javax.security.sasl.AuthorizeCallback. When the JVM's SASL/GSSAPI implementation hands it any other Callback type during client creation or evaluation, it throws UnsupportedCallbackException with this message. It indicates a callback type the client handler does not support was requested.
Source
Thrown at pulsar-client-auth-sasl/src/main/java/org/apache/pulsar/client/impl/auth/PulsarSaslClient.java:128
}
} catch (Exception e) {
log.error().exception(e.getCause()).log("SASL error");
throw new AuthenticationException("SASL/JAAS error" + e.getCause());
}
}
public boolean hasInitialResponse() {
return saslClient.hasInitialResponse();
}
static class ClientCallbackHandler implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof AuthorizeCallback) {
handleAuthorizeCallback((AuthorizeCallback) callback);
} else {
throw new UnsupportedCallbackException(callback, "Unrecognized SASL GSSAPI Client Callback.");
}
}
}
private void handleAuthorizeCallback(AuthorizeCallback ac) {
String authid = ac.getAuthenticationID();
String authzid = ac.getAuthorizationID();
if (authid.equals(authzid)) {
ac.setAuthorized(true);
} else {
ac.setAuthorized(false);
}
if (ac.isAuthorized()) {
ac.setAuthorizedID(authzid);
}
log.info().attr("authenticationID", authid).attr("authorizationID", authzid)
.log("Successfully authenticated");
}View on GitHub (pinned to 820761864e)
Solutions
- Only use this ClientCallbackHandler with the GSSAPI mechanism — GSSAPI obtains credentials from the JAAS Subject and should only ask AuthorizeCallback
- If a different mechanism is required, extend the handler with instanceof branches for the needed callbacks (NameCallback, PasswordCallback, etc.)
- Verify the configured SASL mechanism string is "GSSAPI" (as in PulsarSaslClient's mechs array) and no other provider is picking up the creation
- Check the JVM's SASL provider order so the standard GSSAPI provider handles the mechanism rather than a custom one
Example fix
// before
public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof AuthorizeCallback) {
handleAuthorizeCallback((AuthorizeCallback) callback);
} else {
throw new UnsupportedCallbackException(callback, "Unrecognized SASL GSSAPI Client Callback.");
}
}
}
// after
public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof AuthorizeCallback) {
handleAuthorizeCallback((AuthorizeCallback) callback);
} else if (callback instanceof NameCallback) {
((NameCallback) callback).setName(clientPrincipalName);
} else {
throw new UnsupportedCallbackException(callback, "Unrecognized SASL GSSAPI Client Callback.");
}
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure GSSAPI is the negotiated mechanism so only AuthorizeCallback is delivered
String[] mechs = {"GSSAPI"};
if (!java.util.Arrays.asList(javax.security.sasl.Sasl.getSaslClientFactories().stream()
.flatMap(f -> java.util.Arrays.stream(f.getMechanismNames(new java.util.HashMap<>())))
.toArray(String[]::new)).contains("GSSAPI")) {
throw new IllegalStateException("GSSAPI mechanism not available; handler only supports AuthorizeCallback");
} Type guard
boolean isSupportedCallback(javax.security.auth.callback.Callback c) {
return c instanceof javax.security.sasl.AuthorizeCallback;
} Try / catch
try {
saslClient.evaluateChallenge(token);
} catch (javax.security.sasl.SaslException e) {
if (e.getCause() instanceof javax.security.auth.callback.UnsupportedCallbackException) {
log.error("ClientCallbackHandler received an unsupported callback: {}",
((javax.security.auth.callback.UnsupportedCallbackException) e.getCause()).getCallback().getClass());
// switch mechanism to GSSAPI or extend the handler
}
} Prevention
- Restrict this CallbackHandler to the GSSAPI mechanism only
- Inspect JVM SASL provider order so GSSAPI requests come from the standard provider
- If supporting more mechanisms, add instanceof branches for NameCallback/PasswordCallback/RealmCallback
- Log the callback class name before throwing to speed up diagnosis
When it happens
Trigger: A SASL mechanism or provider (e.g. a non-GSSAPI mechanism, or a provider variant) invoking the handler with callbacks like NameCallback, PasswordCallback, or RealmCallback instead of only AuthorizeCallback; plugging this handler into a different SASL mechanism than GSSAPI.
Common situations: Using the Pulsar SASL client code with a mechanism other than GSSAPI (e.g. SCRAM/DIGEST-MD5 via shared code); a JVM SASL provider that requests extra callbacks; custom provider implementations with non-standard callback requirements.
Related errors
- Unrecognized SASL GSSAPI Server Callback.
- Authentication use SASL/JAAS/GSSAPI but server not have Prin
- Cannot create SASL client with empty JAAS subject principal
- error while booting GSSAPI client
- Cannot create JVM SASL Client
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/83e28e051a492155.
Report an issue: GitHub.