apache/pulsar · error · AuthenticationException

SASL/JAAS error${e.getCause()}

Error message

SASL/JAAS error${e.getCause()}

What it means

evaluateChallenge runs saslClient.evaluateChallenge inside Subject.doAs (JAAS). Any Exception raised there — SaslException from a failed GSSAPI exchange, PrivilegedActionException from the subject, GSS exceptions, IO problems — is caught and rethrown as an AuthenticationException with the message 'SASL/JAAS error' plus the cause. It signals the Kerberos/SASL handshake step itself failed.

Source

Thrown at pulsar-client-auth-sasl/src/main/java/org/apache/pulsar/client/impl/auth/PulsarSaslClient.java:113

        if (saslToken == null) {
            throw new AuthenticationException("saslToken is null");
        }
        try {
            if (clientSubject != null) {
                final byte[] retval = Subject.doAs(clientSubject, new PrivilegedExceptionAction<byte[]>() {
                    @Override
                    public byte[] run() throws SaslException {
                        return saslClient.evaluateChallenge(saslToken.getBytes());
                    }
                });
                return AuthData.of(retval);

            } else {
                return AuthData.of(saslClient.evaluateChallenge(saslToken.getBytes()));
            }
        } 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.");
                }
            }
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Check the logged cause ('SASL error' line) — it names the underlying GSS/SASL problem
  2. Re-run kinit / confirm the keytab is valid and the ticket isn't expired (klist)
  3. Verify client principal in JAAS config matches what the broker authorizes and service principal is correct for the broker host (serverType/hostname)
  4. Ensure clock sync (NTP) between client, broker, and KDC; Kerberos fails with skew
  5. Confirm broker and client negotiate the same SASL mechanism (GSSAPI) and realm

Example fix

// before: token evaluated after ticket expiry without refresh
AuthData resp = saslClient.evaluateChallenge(challenge);

// after: ensure fresh credentials before handshake
if (!subject.getPrincipals().isEmpty() && isTicketExpired(subject)) {
    reloginFromKeytab(); // refresh TGT from keytab
}
AuthData resp = saslClient.evaluateChallenge(challenge);
Defensive patterns

Strategy: try-catch

Validate before calling

// before connecting, validate Kerberos credentials
import org.apache.pulsar.shade.javax.security.auth.kerberos.KerberosTicket;
boolean hasValidTgt(javax.security.auth.Subject s) {
    return s.getPrincipals().size() > 0
        && s.getPrivateCredentials(KerberosTicket.class).stream()
            .anyMatch(t -> !t.isDestroyed() && t.getEndTime().after(new java.util.Date()));
}

Type guard

boolean isKerberosReady(javax.security.auth.Subject s) {
    return s != null && !s.getPrincipals().isEmpty();
}

Try / catch

try {
    AuthData resp = saslClient.evaluateChallenge(challenge);
} catch (javax.naming.AuthenticationException e) {
    // e.getMessage() starts with 'SASL/JAAS error' + cause; refresh creds, then optionally retry handshake
    log.warn("SASL handshake failed: {}", e.getMessage(), e);
    if (isTransient(e)) {
        reloginFromKeytab();
        // rebuild PulsarSaslClient and retry once
    } else {
        throw new IllegalStateException("Kerberos configuration error — fix JAAS/principal/clock", e);
    }
}

Prevention

When it happens

Trigger: Any exception from saslClient.evaluateChallenge(bytes) while processing a broker challenge: corrupt/out-of-order token, GSSAPI context failure, expired Kerberos ticket, clock skew, or doAs failing due to subject/privilege problems.

Common situations: Kerberos ticket expired mid-connection (kinit not refreshed); keytab principal mismatch between client JAAS config and broker; hostname/reverse-DNS mismatch causing GSSAPI service principal mismatch; broker and client SASL mechanism mismatch; clock skew between client and KDC.

Related errors


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