t8y2/dbx · error

invalid ZooKeeper buffer length %d

Error message

invalid ZooKeeper buffer length %d

What it means

When decoding a length-prefixed byte buffer from a ZooKeeper response, a negative length other than the legal -1 (NULL) sentinel means the stream is corrupt or misaligned. The decoder rejects the value in decoder.bytes rather than attempting a take() with a negative size, which would otherwise fail opaquely as io.ErrUnexpectedEOF.

Source

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

func (decoder *zooKeeperDecoder) int64() (int64, error) {
	value, err := decoder.take(8)
	if err != nil {
		return 0, err
	}
	return int64(binary.BigEndian.Uint64(value)), nil
}

func (decoder *zooKeeperDecoder) bytes() ([]byte, error) {
	length, err := decoder.int32()
	if err != nil {
		return nil, err
	}
	if length == -1 {
		return nil, nil
	}
	if length < -1 {
		return nil, fmt.Errorf("invalid ZooKeeper buffer length %d", length)
	}
	value, err := decoder.take(int(length))
	if err != nil {
		return nil, err
	}
	return append([]byte(nil), value...), nil
}

func (decoder *zooKeeperDecoder) string() (string, error) {
	value, err := decoder.bytes()
	return string(value), err
}

func (decoder *zooKeeperDecoder) strings() ([]string, error) {
	length, err := decoder.int32()
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to c0390bff16)

Solutions

  1. Discard the client connection and re-dial — a corrupt length means the byte stream is misaligned and cannot be recovered on the same socket.
  2. Verify the endpoint is a real ZooKeeper server on port 2181 (or your configured port) and that TLS settings match the server.
  3. Check for concurrent use of one client/connection from multiple goroutines — interleaved reads corrupt the framing; serialize access or use one client per goroutine.
  4. Capture the raw bytes (or enable protocol-level logging) around the failure to identify where the stream diverged.

Example fix

// before
resp, err := client.request(payload) // client reused across goroutines

// after
mu.Lock()
defer mu.Unlock()
resp, err := client.request(payload) // serialize access to the shared connection
if err != nil {
    client.Close() // framing is unrecoverable; force re-dial
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// verify the endpoint speaks ZooKeeper before parsing responses
conn, err := net.DialTimeout("tcp", addr, 3*time.Second)
if err != nil {
    return err
}
conn.SetDeadline(time.Now().Add(3 * time.Second))
// a four-byte probe read of a real ZooKeeper reply will never start with an
// implausible length; garbage on connect means wrong service
conn.Close()

Type guard

func isFrameCorruption(err error) bool {
    if err == nil {
        return false
    }
    return strings.Contains(err.Error(), "invalid ZooKeeper buffer length") ||
        errors.Is(err, io.ErrUnexpectedEOF)
}

Try / catch

value, err := client.Get(path)
if isFrameCorruption(err) {
    client.Close()
    client = redial() // stream is misaligned; a fresh connection is required
    value, err = client.Get(path)
}
return value, err

Prevention

When it happens

Trigger: Parsing any response containing a buffer field (node data, SASL payload, stat-associated fields) where the length int32 is < -1: reading from a desynchronized stream, a response produced by a non-ZooKeeper peer, or frame bytes corrupted in transit.

Common situations: Reusing a connection after a previous read timed out mid-frame (offset shifted into the payload); a MITM proxy or port-forward that alters traffic; connecting to a service speaking a different protocol on the ZooKeeper port; driver/server protocol-version mismatch producing shifted fields.

Related errors


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