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
- Check for proxies or connection sharing that could interleave responses on the same socket.
- Reconnect and retry the SASL handshake to re-synchronize XIDs.
- If using a mock server, make it echo the request XID exactly (zooKeeperSASLXIDBase + round).
- 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
- Never share one ZooKeeper connection across concurrent goroutines during handshake.
- Make test fakes echo the exact request XID.
- Re-dial on any XID mismatch; the stream cannot be resynchronized.
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
- ZooKeeper sent an unexpected token after GSSAPI completion
- ZooKeeper sent an unexpected token after GSSAPI completion
- ZooKeeper sent an unexpected token after DIGEST-MD5 completi
- ZooKeeper DIGEST-MD5 completed with an unexpected client tok
- ZooKeeper SASL response is truncated
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/bb1e5606307f1fea.
Report an issue: GitHub.