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

  1. Reconnect and restart the SASL handshake — the stream is likely corrupted or truncated.
  2. Inspect intermediate proxies/LBs that may truncate or rewrite payloads.
  3. If using a mock server, write the correct token length and the matching number of token bytes.
  4. 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

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


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