t8y2/dbx · error

continue ZooKeeper DIGEST-MD5 negotiation at round %d: %w

Error message

continue ZooKeeper DIGEST-MD5 negotiation at round %d: %w

What it means

After receiving a challenge, the client calls saslClient.Step(challenge) to compute the next response. If Step rejects the challenge (malformed rspauth, protocol violation, wrong credentials), the error is wrapped as "continue ZooKeeper DIGEST-MD5 negotiation at round %d: %w". This is a client-side cryptographic/protocol rejection, not a network error.

Source

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

	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
		}
	}
	return fmt.Errorf("ZooKeeper DIGEST-MD5 negotiation exceeded %d rounds", zooKeeperSASLMaxRounds)
}

func zooKeeperSASLRound(connection net.Conn, xid int32, token []byte) ([]byte, error) {
	payload := make([]byte, 12+len(token))
	binary.BigEndian.PutUint32(payload[0:4], uint32(xid))
	binary.BigEndian.PutUint32(payload[4:8], uint32(zooKeeperSASLOpcode))
	binary.BigEndian.PutUint32(payload[8:12], uint32(len(token)))
	copy(payload[12:], token)
	if err := writeZooKeeperFrame(connection, payload); err != nil {

View on GitHub (pinned to c0390bff16)

Solutions

  1. Re-check the digest username/password — bad credentials most commonly make Step fail.
  2. Compare qop/realm/cipher settings negotiated with the server's SASL configuration.
  3. Look at the wrapped inner error for the precise Step failure reason.
  4. Ensure both client and server use standard DIGEST-MD5 semantics; upgrade the SASL client library if it deviates.
Defensive patterns

Strategy: validation

Validate before calling

// Verify credentials before starting; bad secrets are the most common Step() failure.
if creds.User == "" || creds.Password == "" { return errors.New("empty SASL credentials") }

Try / catch

err := negotiateSASLDigest(conn, creds)
if err != nil && strings.Contains(err.Error(), "continue ZooKeeper DIGEST-MD5 negotiation") {
	return fmt.Errorf("check DIGEST-MD5 credentials/qop settings: %w", err)
}

Prevention

When it happens

Trigger: negotiateSASLDigest's loop calls saslClient.Step(challenge) at round N and Step returns an error, typically because the server's challenge is not a valid DIGEST-MD5 response for the given credentials.

Common situations: Wrong username/password (the server's rspauth check fails), server using a different qop/realm configuration than the client expects, or an incompatible SASL implementation on one side.

Related errors


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