{"record":{"id":"6ebe57a9340bcdf8","repo":"apache/pulsar","slug":"sasl-jaas-error-e-getcause","errorCode":null,"errorMessage":"SASL/JAAS error${e.getCause()}","messagePattern":"SASL/JAAS error(.+?)","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-client-auth-sasl/src/main/java/org/apache/pulsar/client/impl/auth/PulsarSaslClient.java","lineNumber":113,"sourceCode":"        if (saslToken == null) {\n            throw new AuthenticationException(\"saslToken is null\");\n        }\n        try {\n            if (clientSubject != null) {\n                final byte[] retval = Subject.doAs(clientSubject, new PrivilegedExceptionAction<byte[]>() {\n                    @Override\n                    public byte[] run() throws SaslException {\n                        return saslClient.evaluateChallenge(saslToken.getBytes());\n                    }\n                });\n                return AuthData.of(retval);\n\n            } else {\n                return AuthData.of(saslClient.evaluateChallenge(saslToken.getBytes()));\n            }\n        } catch (Exception e) {\n            log.error().exception(e.getCause()).log(\"SASL error\");\n            throw new AuthenticationException(\"SASL/JAAS error\" + e.getCause());\n        }\n    }\n\n    public boolean hasInitialResponse() {\n        return saslClient.hasInitialResponse();\n    }\n\n    static class ClientCallbackHandler implements CallbackHandler {\n        @Override\n        public void handle(Callback[] callbacks) throws UnsupportedCallbackException {\n            for (Callback callback : callbacks) {\n                if (callback instanceof AuthorizeCallback) {\n                    handleAuthorizeCallback((AuthorizeCallback) callback);\n                } else {\n                    throw new UnsupportedCallbackException(callback, \"Unrecognized SASL GSSAPI Client Callback.\");\n                }\n            }\n        }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-auth-sasl/src/main/java/org/apache/pulsar/client/impl/auth/PulsarSaslClient.java#L95-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the logged cause ('SASL error' line) — it names the underlying GSS/SASL problem","Re-run kinit / confirm the keytab is valid and the ticket isn't expired (klist)","Verify client principal in JAAS config matches what the broker authorizes and service principal is correct for the broker host (serverType/hostname)","Ensure clock sync (NTP) between client, broker, and KDC; Kerberos fails with skew","Confirm broker and client negotiate the same SASL mechanism (GSSAPI) and realm"],"exampleFix":"// before: token evaluated after ticket expiry without refresh\nAuthData resp = saslClient.evaluateChallenge(challenge);\n\n// after: ensure fresh credentials before handshake\nif (!subject.getPrincipals().isEmpty() && isTicketExpired(subject)) {\n    reloginFromKeytab(); // refresh TGT from keytab\n}\nAuthData resp = saslClient.evaluateChallenge(challenge);","handlingStrategy":"try-catch","validationCode":"// before connecting, validate Kerberos credentials\nimport org.apache.pulsar.shade.javax.security.auth.kerberos.KerberosTicket;\nboolean hasValidTgt(javax.security.auth.Subject s) {\n    return s.getPrincipals().size() > 0\n        && s.getPrivateCredentials(KerberosTicket.class).stream()\n            .anyMatch(t -> !t.isDestroyed() && t.getEndTime().after(new java.util.Date()));\n}","typeGuard":"boolean isKerberosReady(javax.security.auth.Subject s) {\n    return s != null && !s.getPrincipals().isEmpty();\n}","tryCatchPattern":"try {\n    AuthData resp = saslClient.evaluateChallenge(challenge);\n} catch (javax.naming.AuthenticationException e) {\n    // e.getMessage() starts with 'SASL/JAAS error' + cause; refresh creds, then optionally retry handshake\n    log.warn(\"SASL handshake failed: {}\", e.getMessage(), e);\n    if (isTransient(e)) {\n        reloginFromKeytab();\n        // rebuild PulsarSaslClient and retry once\n    } else {\n        throw new IllegalStateException(\"Kerberos configuration error — fix JAAS/principal/clock\", e);\n    }\n}","preventionTips":["Run kinit or keytab-based auto-relogin before starting long-lived clients","Sync clocks (NTP) across client, broker, and KDC","Verify service principal hostnames resolve and match broker reverse DNS","Keep client JAAS principal consistent with broker's authorized principals","Catch AuthenticationException and rebuild the SASL client rather than reusing a failed handshake"],"tags":["sasl","kerberos","jaas","authentication"],"backgroundTag":"kerberos-handshake-failed","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}