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.

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.

Appendix: 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)