t8y2/dbx · error

ZooKeeper SASL response xid %d does not match request xid %d

Error message

ZooKeeper SASL response xid %d does not match request xid %d

What it means

Every SASL request carries an XID echoed by the server in its response. zooKeeperSASLRound compares responseXID (bytes 4:8) with the request XID; a mismatch means the reply does not correspond to this round's request — a desynchronized stream. The error includes both XIDs for diagnosis.

Source

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

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 {
		return nil, err
	}
	response, err := readZooKeeperFrame(connection)
	if err != nil {
		return nil, err
	}
	if len(response) < 20 {
		return nil, errors.New("ZooKeeper SASL response is truncated")
	}
	responseXID := int32(binary.BigEndian.Uint32(response[4:8]))
	if responseXID != xid {
		return nil, fmt.Errorf("ZooKeeper SASL response xid %d does not match request xid %d", responseXID, xid)
	}
	errorCode := int32(binary.BigEndian.Uint32(response[16:20]))
	if errorCode != 0 {
		return nil, fmt.Errorf("ZooKeeper SASL server returned error %d", errorCode)
	}
	if len(response) < 24 {
		return nil, errors.New("ZooKeeper SASL token is truncated")
	}
	tokenLength := int(int32(binary.BigEndian.Uint32(response[20:24])))
	if tokenLength < 0 || tokenLength > zooKeeperMaximumFrameLen || 24+tokenLength > len(response) {
		return nil, fmt.Errorf("ZooKeeper SASL token length %d is invalid", tokenLength)
	}
	return append([]byte(nil), response[24:24+tokenLength]...), nil
}

func readZooKeeperFrame(reader io.Reader) ([]byte, error) {
	header := make([]byte, 4)
	if _, err := io.ReadFull(reader, header); err != nil {

View on GitHub (pinned to c0390bff16)

Solutions

  1. Check for proxies or connection sharing that could interleave responses on the same socket.
  2. Reconnect and retry the SASL handshake to re-synchronize XIDs.
  3. If using a mock server, make it echo the request XID exactly (zooKeeperSASLXIDBase + round).
  4. Verify no concurrent goroutines are reading from the same connection, shifting frame boundaries.

Example fix

// before (fake server)
binary.BigEndian.PutUint32(resp[4:8], uint32(99))
// after
binary.BigEndian.PutUint32(resp[4:8], uint32(requestXID))
Defensive patterns

Strategy: try-catch

Try / catch

_, err := zooKeeperSASLRound(conn, xid, token)
if err != nil && strings.Contains(err.Error(), "does not match request xid") {
	conn.Close()
	return retryWithFreshConnection()
}

Prevention

When it happens

Trigger: negotiateSASLDigest or TestZooKeeperSASLRound calls zooKeeperSASLRound; the server (or test fake) returns a response whose big-endian uint32 at offset 4 differs from the request's xid (zooKeeperSASLXIDBase + round).

Common situations: A proxy multiplexing/pipelining frames incorrectly, a server bug emitting responses out of order, or a test mock replaying a stale response — also triggered deliberately by TestZooKeeperSASLRoundRejectsWrongXID.

Related errors


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