apache/pulsar · error · SaslException
Cannot create SASL client with empty JAAS subject principal
Error message
Cannot create SASL client with empty JAAS subject principal
What it means
PulsarSaslClient's constructor validates that the JAAS Subject used as the client identity has at least one Principal; an empty principal set means the subject carries no authenticated identity (no Kerberos user), so the GSSAPI mechanism cannot be built. It throws SaslException with this message.
Source
Thrown at pulsar-client-auth-sasl/src/main/java/org/apache/pulsar/client/impl/auth/PulsarSaslClient.java:60
* This is added for support Kerberos authentication.
*/
@CustomLog
public class PulsarSaslClient {
private final SaslClient saslClient;
private final Subject clientSubject;
public PulsarSaslClient(String serverHostname, String serverType, Subject subject) throws SaslException {
checkArgument(subject != null, "Cannot create SASL client with NULL JAAS subject");
checkArgument(!Strings.isNullOrEmpty(serverHostname), "Cannot create SASL client with NUll server name");
if (!serverType.equals(SaslConstants.SASL_BROKER_PROTOCOL) && !serverType
.equals(SaslConstants.SASL_PROXY_PROTOCOL)) {
log.warn().attr("serverType", serverType).log("The server type is not recommended");
}
String serverPrincipal = serverType.toLowerCase() + "/" + serverHostname;
this.clientSubject = subject;
if (clientSubject.getPrincipals().isEmpty()) {
throw new SaslException("Cannot create SASL client with empty JAAS subject principal");
}
// GSSAPI/Kerberos
final Object[] principals = clientSubject.getPrincipals().toArray();
final Principal clientPrincipal = (Principal) principals[0];
final KerberosName clientKerberosName = new KerberosName(clientPrincipal.getName());
KerberosName serviceKerberosName = new KerberosName(serverPrincipal + "@" + clientKerberosName.getRealm());
final String serviceName = serviceKerberosName.getServiceName();
final String serviceHostname = serviceKerberosName.getHostName();
final String clientPrincipalName = clientKerberosName.toString();
log.info().attr("serverPrincipal", serverPrincipal)
.log("Using JAAS/SASL/GSSAPI auth to connect to server");
try {
this.saslClient = Subject.doAs(clientSubject, new PrivilegedExceptionAction<SaslClient>() {
@Override
public SaslClient run() throws SaslException {
String[] mechs = {"GSSAPI"};View on GitHub (pinned to 820761864e)
Solutions
- Verify the JAAS login actually produces a principal: after LoginContext.login(), assert !subject.getPrincipals().isEmpty() before constructing PulsarSaslClient.
- Fix the JAAS config file: ensure the section (default PulsarClient) uses com.sun.security.auth.module.Krb5LoginModule with required/ sufficient flag and a valid keyTab/principal.
- Confirm the keytab exists and is readable, and that kinit/klist shows valid credentials.
- Check that login failure isn't being swallowed — JAAS modules with optional flag won't throw but yield an empty subject.
Example fix
// before
Subject subject = new Subject(); // empty, no login performed
PulsarSaslClient client = new PulsarSaslClient(host, "broker", subject); // SaslException
// after
LoginContext lc = new LoginContext("PulsarClient");
lc.login();
Subject subject = lc.getSubject();
if (subject.getPrincipals().isEmpty()) { throw new IllegalStateException("JAAS login produced no principal"); }
PulsarSaslClient client = new PulsarSaslClient(host, "broker", subject); Defensive patterns
Strategy: validation
Validate before calling
// Java: verify the JAAS subject has principals before constructing PulsarSaslClient
static void validateSubject(Subject subject) {
if (subject == null || subject.getPrincipals().isEmpty()) {
throw new IllegalStateException("JAAS subject has no principals; login failed or was skipped");
}
} Try / catch
try {
PulsarSaslClient client = new PulsarSaslClient(host, serverType, subject);
} catch (SaslException e) {
if (e.getMessage().contains("empty JAAS subject principal")) {
throw new IllegalStateException("Kerberos login produced no principal — check JAAS config and kinit", e);
}
throw e;
} Prevention
- After LoginContext.login(), assert !subject.getPrincipals().isEmpty().
- Use required/sufficient (not optional) flags for Krb5LoginModule so failed login throws instead of yielding an empty subject.
- Verify keytab path/permissions and run klist before starting the client.
- Never pass a manually constructed new Subject() without a completed login.
When it happens
Trigger: Constructing PulsarSaslClient with a Subject produced by a failed or no-op JAAS login — e.g. the LoginContext logged in a subject with no principals because the Krb5LoginModule was skipped (useKeyTab=false with no ticket, debug misconfig) or the login silently produced an empty subject.
Common situations: Kerberos keytab path wrong or unreadable so login falls through without a principal; JAAS section misconfigured (wrong principal/debug flags) causing login to succeed with zero principals; passing new Subject() or a subject created without doAsPrivileged login; expired TGT combined with a module configured not to fail.
Related errors
- SASL/JAAS error${e.getCause()}
- Authentication use SASL/JAAS/GSSAPI but server not have Prin
- Unrecognized SASL GSSAPI Server Callback.
- loginContext name (JAAS file section header) was null. Pleas
- Kerberos authentication without KerberosTicket provided!
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/969dc4692fff506f.
Report an issue: GitHub.