t8y2/dbx · error

decode ZooKeeper SASL round %d: %w

Error message

decode ZooKeeper SASL round %d: %w

What it means

This error occurs when the client cannot decode the SASL challenge bytes from a ZooKeeper SASL response frame. After each SASL request, authenticateSASL parses the reply expecting a length-prefixed token (the GSSAPI challenge); a decode failure means the response payload is shorter or malformed relative to the expected wire format. It usually signals a protocol mismatch or a corrupt/abridged response.

Source

Thrown at agents/drivers/argo-go/zookeeper_protocol.go:216

	token, err := saslClient.Start()
	if err != nil {
		return fmt.Errorf("start ZooKeeper GSSAPI negotiation: %w", err)
	}
	for round := 0; round < zooKeeperMaxSASLRounds; round++ {
		response, requestErr := client.request(zooKeeperOpSASL, func(encoder *zooKeeperEncoder) {
			if token == nil {
				encoder.bytes([]byte{})
				return
			}
			encoder.bytes(token)
		})
		if requestErr != nil {
			return fmt.Errorf("ZooKeeper SASL round %d: %w", round+1, requestErr)
		}
		decoder := newZooKeeperDecoder(response)
		challenge, decodeErr := decoder.bytes()
		if decodeErr != nil {
			return fmt.Errorf("decode ZooKeeper SASL round %d: %w", round+1, decodeErr)
		}
		if saslClient.Complete() {
			if len(challenge) != 0 {
				return errors.New("ZooKeeper sent an unexpected token after GSSAPI completion")
			}
			return nil
		}
		token, err = saslClient.Step(challenge)
		if err != nil {
			return fmt.Errorf("continue ZooKeeper GSSAPI negotiation at round %d: %w", round+1, err)
		}
	}
	return fmt.Errorf("ZooKeeper GSSAPI negotiation exceeded %d rounds", zooKeeperMaxSASLRounds)
}

func (client *protocolZooKeeperClient) AddAuth(scheme string, auth []byte) error {
	_, err := client.request(zooKeeperOpSetAuth, func(encoder *zooKeeperEncoder) {
		encoder.int32(0)

View on GitHub (pinned to c0390bff16)

Solutions

  1. Read the wrapped inner decode error to see whether the frame length exceeds the payload (truncation) — then check for proxies/NAT in front of ZooKeeper and connect directly to the quorum.
  2. Confirm the ZooKeeper server version supports the SASL opcode with the same framing the client implements; upgrade the server or the client driver to matching versions.
  3. Close and re-establish the connection: a desynced TCP stream after an earlier failure will keep producing malformed frames.
  4. Check server logs for errors around the SASL round to see what the server actually replied.
  5. Retry with a longer timeout in case a partial read due to a short deadline produced a short frame.

Example fix

// before: proxy strips frames
connectErr := connect("zk-lb.internal:2181") // decode ZooKeeper SASL round 1: unexpected EOF
// after: connect directly to quorum members
connectErr := connect("zk-1.internal:2181,zk-2.internal:2181,zk-3.internal:2181")
Defensive patterns

Strategy: retry

Try / catch

err := client.Get(path)
if err != nil && strings.Contains(err.Error(), "decode ZooKeeper SASL round") {
    // malformed frame — stream may be corrupt; do NOT reuse the connection
    client.Close()
    return redialAndRetry(path, 1)
}
return err

Prevention

When it happens

Trigger: During authenticateSASL, decoder.bytes() on the response of a zooKeeperOpSASL request fails because the response payload contains fewer bytes than its declared length prefix or is empty — e.g. an incompatible ZooKeeper server version, a proxy that truncates frames, or a server replying with an unexpected error body instead of a SASL token.

Common situations: Connecting through a load balancer/proxy that mangles ZooKeeper frames, mismatched ZooKeeper server protocol (older server not supporting the SASL opcode body format the client expects), or corrupted responses when the connection state desyncs after a prior failed request on the same TCP connection.

Related errors


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