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
- Read the wrapped inner error and the round number to see whether it's I/O, protocol, or a server error code.
- Verify the server actually has SASL enabled (zoo.cfg: authProvider + sessionRequireClientSASLAuth as appropriate).
- Check network path (proxies, LB idle timeouts) between client and ZooKeeper on port 2181.
- Retry authenticateSASLDigest if the cause was a transient connection drop.
- 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
- Set reasonable connection deadlines and dial fresh connections on handshake failure.
- Confirm server SASL config (authProvider, JAAS) before deploying clients.
- Avoid proxies that idle-timeout mid-handshake, or raise their timeouts.
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
- ZooKeeper SASL round %d: %w
- ZooKeeper session closed because SASL authentication is requ
- ZooKeeper authentication failed
- ZooKeeper session closed because SASL authentication is requ
- username is required when auth_scheme = "sasl_digest"
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/40b0d9320bca4ae3.
Report an issue: GitHub.