apache/pulsar · error · UnsupportedCallbackException
Unrecognized SASL GSSAPI Server Callback.
Error message
Unrecognized SASL GSSAPI Server Callback.
What it means
UnsupportedCallbackException thrown by PulsarSaslServer.SaslServerCallbackHandler.handle when the SASL framework presents a Callback type the handler does not recognize. This server-side handler only supports AuthorizeCallback, which is what the GSSAPI mechanism supplies; any other callback type (e.g. NameCallback, PasswordCallback from a mechanism mismatch) reaches the default branch and throws.
Source
Thrown at pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/PulsarSaslServer.java:160
log.error().exception(e).log("response: Failed to evaluate client token");
throw new AuthenticationException(e.getMessage());
}
}
static class SaslServerCallbackHandler implements CallbackHandler {
Pattern allowedIdsPattern;
public SaslServerCallbackHandler(Pattern pattern) {
this.allowedIdsPattern = pattern;
}
@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 Server Callback.");
}
}
}
private void handleAuthorizeCallback(AuthorizeCallback ac) {
String authenticationID = ac.getAuthenticationID();
String authorizationID = ac.getAuthorizationID();
if (!authenticationID.equals(authorizationID)) {
ac.setAuthorized(false);
log.info().attr("authenticationID", authenticationID).attr("authorizationID", authorizationID)
.log("Forbidden access to client");
return;
}
if (!allowedIdsPattern.matcher(authenticationID).matches()) {
ac.setAuthorized(false);
log.info()
.attr("authenticationID", authenticationID)
.attr("property", SaslConstants.JAAS_CLIENT_ALLOWED_IDS)View on GitHub (pinned to 820761864e)
Solutions
- Ensure the SaslServer is created strictly for the GSSAPI mechanism (Sasl.createSaslServer("GSSAPI", ...) as done in PulsarSaslServer.createSaslServer) and the client negotiates GSSAPI/Kerberos only
- Check client SASL config (jaas.conf / mechanism properties) so it does not fall back to PLAIN or DIGEST-MD5 against this broker
- If you extended the handler for other mechanisms, add instanceof branches for the expected callback types (NameCallback, PasswordCallback, etc.) before the throw
- Remove/verify any custom SaslServerFactory or security.provider registrations that could alter the callback list
Example fix
// before
if (callback instanceof AuthorizeCallback) {
handleAuthorizeCallback((AuthorizeCallback) callback);
} else {
throw new UnsupportedCallbackException(callback, "Unrecognized SASL GSSAPI Server Callback.");
}
// after
if (callback instanceof AuthorizeCallback) {
handleAuthorizeCallback((AuthorizeCallback) callback);
} else if (callback instanceof NameCallback) {
((NameCallback) callback).setName("");
} else {
throw new UnsupportedCallbackException(callback, "Unrecognized SASL GSSAPI Server Callback.");
} Defensive patterns
Strategy: try-catch
Validate before calling
// confirm the client negotiates GSSAPI only
if (!saslMechanism.equals("GSSAPI")) {
throw new IllegalArgumentException("Server supports GSSAPI only; got " + saslMechanism);
} Type guard
boolean isSupportedCallback(Callback c) {
return c instanceof AuthorizeCallback;
} Try / catch
try {
return AuthData.of(saslServer.evaluateResponse(token.getBytes()));
} catch (SaslException e) {
Throwable cause = e;
while ((cause = cause.getCause()) != null) {
if (cause instanceof UnsupportedCallbackException) {
throw new AuthenticationException("SASL mechanism/callback mismatch; client must use GSSAPI");
}
}
throw new AuthenticationException(e.getMessage());
} Prevention
- Keep client and broker SASL mechanism configuration aligned (both Kerberos/GSSAPI)
- Do not register custom Sasl factories or providers that add callbacks to the GSSAPI exchange
- When extending the handler to new mechanisms, add explicit instanceof branches for every callback the mechanism requests
- Test SASL handshake end-to-end after any JDK upgrade, as GSSAPI callback lists can vary
When it happens
Trigger: SaslServer.evaluateResponse (via PulsarSaslServer.response) triggers the underlying SaslServer's callback handler with a callback other than AuthorizeCallback — typically when the negotiated/configured mechanism is not plain GSSAPI (e.g. SASL mechanism negotiation picks DIGEST-MD5/PLAIN which request NameCallback/PasswordCallback), or a custom Sasl factory injects extra callbacks.
Common situations: Client and broker disagree on SASL mechanism (client configured for a mechanism requiring name/password callbacks while the server was built for GSSAPI); a custom SaslServerFactory is on the classpath adding callbacks; broker code modified to create the SaslServer with additional mechanism properties.
Related errors
- Authentication use SASL/JAAS/GSSAPI but server not have Prin
- Cannot create SASL client with empty JAAS subject principal
- SASL/JAAS error${e.getCause()}
- Unrecognized SASL GSSAPI Client Callback.
- Kerberos authentication without KerberosTicket provided!
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/e49d32365fc8559f.
Report an issue: GitHub.