apache/cassandra · error · AuthenticationException

SASL negotiation not complete

Error message

SASL negotiation not complete

What it means

PasswordAuthenticator's SASLPlainAuthenticator tracks whether the full PLAIN SASL exchange (the client's response containing authzid/authcid/password) has been received. getAuthenticatedUser() refuses to return a user until evaluateResponse() has completed the negotiation and the authenticator state is 'complete'. Throwing AuthenticationException here prevents reading credentials before they were decoded.

Source

Thrown at src/java/org/apache/cassandra/auth/PasswordAuthenticator.java:309

        private String username;
        private String password;

        public byte[] evaluateResponse(byte[] clientResponse) throws AuthenticationException
        {
            decodeCredentials(clientResponse);
            complete = true;
            return null;
        }

        public boolean isComplete()
        {
            return complete;
        }

        public AuthenticatedUser getAuthenticatedUser() throws AuthenticationException
        {
            if (!complete)
                throw new AuthenticationException("SASL negotiation not complete");
            return authenticate(username, password);
        }

        @Override
        public AuthenticationMode getAuthenticationMode()
        {
            return AuthenticationMode.PASSWORD;
        }

        /**
         * SASL PLAIN mechanism specifies that credentials are encoded in a
         * sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL).
         * The form is : {@code authzId<NUL>authnId<NUL>password<NUL>}
         * authzId is optional, and in fact we don't care about it here as we'll
         * set the authzId to match the authnId (that is, there is no concept of
         * a user being authorized to act on behalf of another with this IAuthenticator).
         *
         * @param bytes encoded credentials string sent by the client

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Ensure the client completes the SASL PLAIN handshake: send the AuthResponse token (NUL-separated authzid/authcid/password) before the server queries the user
  2. In server-side/custom code, call evaluateResponse(byte[]) first and only call getAuthenticatedUser() when it signals completion
  3. Check driver authentication configuration (authenticator class, credentials provider) so the handshake is not aborted early

Example fix

// before
AuthenticatedUser user = negotiator.getAuthenticatedUser(); // throws if handshake incomplete
// after
byte[] response = buildPlainToken(user, pass);
if (negotiator.evaluateResponse(response) instanceof AuthenticationException)
    throw new IllegalStateException("SASL handshake failed");
AuthenticatedUser user = negotiator.getAuthenticatedUser();
Defensive patterns

Strategy: try-catch

Validate before calling

if (!negotiatorComplete(negotiator)) throw new IllegalStateException("call evaluateResponse first");

Type guard

boolean saslComplete(SaslNegotiator n) { return n != null && n.getAuthenticationMode() != null && completed(n); }

Try / catch

try { user = negotiator.getAuthenticatedUser(); } catch (AuthenticationException e) { handleAuthRequired(); }

Prevention

When it happens

Trigger: Calling SaslNegotiator.getAuthenticatedUser() before any call to evaluateResponse(), or after a negotiation that threw AuthenticationException (leaving complete=false). Typically from the native-protocol auth flow when the server asks for the authenticated user at the wrong lifecycle point.

Common situations: Custom client drivers skipping the SASL response step; middleware or test code instantiating PasswordAuthenticator.newSaslNegotiator() and directly querying the user; a failed/cancelled SASL handshake followed by a user lookup.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/077b6f82014cbc3a. Report an issue: GitHub.