apache/pulsar · error · AuthenticationException
saslToken is null
Error message
saslToken is null
What it means
PulsarSaslClient.evaluateChallenge(saslToken) requires the server-provided SASL challenge bytes wrapped in an AuthData object. When the caller passes null, the method immediately throws this AuthenticationException. This is a defensive argument check — the SASL protocol expects each challenge from the broker to carry a token.
Source
Thrown at pulsar-client-auth-sasl/src/main/java/org/apache/pulsar/client/impl/auth/PulsarSaslClient.java:96
String[] mechs = {"GSSAPI"};
return Sasl.createSaslClient(mechs, clientPrincipalName, serviceName, serviceHostname, null,
new ClientCallbackHandler());
}
});
} catch (PrivilegedActionException err) {
log.error().exception(err.getCause()).log("GSSAPI client error");
throw new SaslException("error while booting GSSAPI client", err.getCause());
}
if (saslClient == null) {
throw new SaslException("Cannot create JVM SASL Client");
}
}
public AuthData evaluateChallenge(final AuthData saslToken) throws AuthenticationException {
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());
}View on GitHub (pinned to 820761864e)
Solutions
- Ensure every challenge passed to evaluateChallenge is a non-null AuthData (e.g. AuthData.INIT from the client for the first step)
- Inspect the Authentication/AuthenticationDataProvider implementation feeding the token; fix code paths that return null
- If the broker sent no token, verify broker-side SASL configuration and the client's authParams (JAAS subject) are consistent
- For custom plugins, default the first token to AuthData.of(new byte[0]) or AuthData.INIT rather than null
Example fix
// before authData = saslClient.evaluateChallenge(challenge); // challenge may be null // after authData = saslClient.evaluateChallenge(challenge == null ? AuthData.INIT : challenge);
Defensive patterns
Strategy: validation
Validate before calling
// before calling evaluateChallenge
if (saslToken == null) {
throw new IllegalArgumentException("SASL challenge token must not be null; use AuthData.INIT for the first step");
}
AuthData resp = saslClient.evaluateChallenge(saslToken); Type guard
boolean isValidAuthData(AuthData d) {
return d != null && d.getBytes() != null;
} Prevention
- Always bootstrap the SASL exchange with AuthData.INIT or an empty AuthData, never null
- Audit custom Authentication implementations for null-returning getAuthData paths
- Unit-test the full challenge sequence (init -> challenge -> response) against a stubbed broker
When it happens
Trigger: Calling evaluateChallenge(null) directly; an Authentication/AuthenticationDataProvider path that produces a null AuthData for a challenge step; wiring a custom Authentication implementation into the SASL flow that returns null on some handshake stage.
Common situations: Custom auth plugin integration where authenticate()/getAuthData returns null before delegating to PulsarSaslClient; broker sends an empty/unexpected challenge and the client-side adapter maps it to null; unit tests invoking evaluateChallenge directly without constructing AuthData.
Related errors
- Authentication use SASL/JAAS/GSSAPI but server not have Prin
- Unrecognized SASL GSSAPI Server Callback.
- Invalid token string, missing attributes
- Invalid authentication token
- Invalid signed text:
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/7c11b94be08e4efa.
Report an issue: GitHub.