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

  1. 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
  2. Check client SASL config (jaas.conf / mechanism properties) so it does not fall back to PLAIN or DIGEST-MD5 against this broker
  3. If you extended the handler for other mechanisms, add instanceof branches for the expected callback types (NameCallback, PasswordCallback, etc.) before the throw
  4. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/e49d32365fc8559f. Report an issue: GitHub.