t8y2/dbx · error

ZooKeeper SASL round %d: %w

Error message

ZooKeeper SASL round %d: %w

What it means

During the DIGEST-MD5 challenge/response loop, each call to zooKeeperSASLRound (a network round trip carrying the SASL exchange) that fails is wrapped as "ZooKeeper SASL round %d: %w". This means the protocol exchange with the server broke at a specific round — I/O error, malformed server reply, frame error, or a server error code. The round number (1-based) is included to locate the failure.

Source

Thrown at agents/drivers/zookeeper/sasl.go:97

}

func negotiateSASLDigest(connection net.Conn, timeout time.Duration, saslClient saslClient) error {
	if timeout <= 0 {
		timeout = defaultConnectionTimeout
	}
	if err := connection.SetDeadline(time.Now().Add(timeout)); err != nil {
		return err
	}
	defer connection.SetDeadline(time.Time{})

	token, err := saslClient.Start()
	if err != nil {
		return fmt.Errorf("start ZooKeeper DIGEST-MD5 negotiation: %w", err)
	}
	for round := 0; round < zooKeeperSASLMaxRounds; round++ {
		challenge, err := zooKeeperSASLRound(connection, zooKeeperSASLXIDBase+int32(round), token)
		if err != nil {
			return fmt.Errorf("ZooKeeper SASL round %d: %w", round+1, err)
		}
		if saslClient.Complete() {
			if len(challenge) != 0 {
				return errors.New("ZooKeeper sent an unexpected token after DIGEST-MD5 completion")
			}
			return nil
		}
		token, err = saslClient.Step(challenge)
		if err != nil {
			return fmt.Errorf("continue ZooKeeper DIGEST-MD5 negotiation at round %d: %w", round+1, err)
		}
		if saslClient.Complete() {
			if len(token) != 0 {
				return errors.New("ZooKeeper DIGEST-MD5 completed with an unexpected client token")
			}
			return nil
		}
	}

View on GitHub (pinned to c0390bff16)

Solutions

  1. Read the wrapped inner error and the round number to see whether it's I/O, protocol, or a server error code.
  2. Verify the server actually has SASL enabled (zoo.cfg: authProvider + sessionRequireClientSASLAuth as appropriate).
  3. Check network path (proxies, LB idle timeouts) between client and ZooKeeper on port 2181.
  4. Retry authenticateSASLDigest if the cause was a transient connection drop.
  5. Confirm credentials match the server's digest store; repeated rejections surface here as server error codes.
Defensive patterns

Strategy: retry

Validate before calling

if conn == nil { return errors.New("connection required before SASL negotiation") }

Try / catch

err := negotiateSASLDigest(conn, creds)
if err != nil {
	if errors.Is(err, io.ErrUnexpectedEOF) || isNetTimeout(err) {
		conn.Close(); conn = dial(); err = negotiateSASLDigest(conn, creds)
	}
	if err != nil { return fmt.Errorf("sasl handshake: %w", err) }
}

Prevention

When it happens

Trigger: negotiateSASLDigest, invoked by authenticateSASLDigest, calls zooKeeperSASLRound for round N and it returns an error (connection drop, truncated frame, wrong xid, invalid token length, or nonzero server error code).

Common situations: Server closes the connection mid-handshake after rejecting credentials, a proxy/load balancer drops the connection, network instability, or the server does not actually support SASL and replies with an error code.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/40b0d9320bca4ae3. Report an issue: GitHub.