t8y2/dbx · error
ZooKeeper SASL token length %d is invalid
Error message
ZooKeeper SASL token length %d is invalid
What it means
The SASL response carries a token length at bytes 20:24. zooKeeperSASLRound validates it: negative, larger than zooKeeperMaximumFrameLen, or extending past the received payload makes the response unusable, and this error names the offending length. It protects against corrupt or malicious replies causing invalid slices.
Source
Thrown at agents/drivers/zookeeper/sasl.go:148
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 {
return nil, err
}
length := int(int32(binary.BigEndian.Uint32(header)))
if length < 0 || length > zooKeeperMaximumFrameLen {
return nil, fmt.Errorf("ZooKeeper frame length %d is invalid", length)
}
payload := make([]byte, length+4)
copy(payload, header)
if _, err := io.ReadFull(reader, payload[4:]); err != nil {
return nil, err
}View on GitHub (pinned to c0390bff16)
Solutions
- Reconnect and restart the SASL handshake — the stream is likely corrupted or truncated.
- Inspect intermediate proxies/LBs that may truncate or rewrite payloads.
- If using a mock server, write the correct token length and the matching number of token bytes.
- Ensure no other reader is consuming bytes from the connection concurrently, causing short reads.
Example fix
// before (fake server) binary.BigEndian.PutUint32(resp[20:24], uint32(999999)) // after binary.BigEndian.PutUint32(resp[20:24], uint32(len(token))) resp = append(resp, token...)
Defensive patterns
Strategy: validation
Validate before calling
func validSASLResponse(resp []byte) bool {
if len(resp) < 24 { return false }
tl := int(int32(binary.BigEndian.Uint32(resp[20:24])))
return tl >= 0 && tl <= zooKeeperMaximumFrameLen && 24+tl <= len(resp)
} Try / catch
token, err := zooKeeperSASLRound(conn, xid, token)
if err != nil {
conn.Close() // framing corruption: connection is unusable
return fmt.Errorf("sasl round failed: %w", err)
} Prevention
- Prefer strict length validation before any slicing of server responses.
- Drop the connection on any framing anomaly — bytes are misaligned.
- Test fake servers should compute the length field from the actual token.
When it happens
Trigger: zooKeeperSASLRound parses a response whose tokenLength field is negative, exceeds zooKeeperMaximumFrameLen, or where 24+tokenLength > len(response) — a truncated or corrupted SASL token.
Common situations: Corrupted stream from a misbehaving proxy, a server bug emitting a short frame, or a test fake constructing a malformed token; also caused by earlier frame misalignment from missed bytes.
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/5f132cf0a8709e17.
Report an issue: GitHub.