t8y2/dbx · error

ZooKeeper response frame is %d bytes, maximum is %d

Error message

ZooKeeper response frame is %d bytes, maximum is %d

What it means

readFrame reads a 4-byte big-endian length prefix from the ZooKeeper connection, then reads exactly that many payload bytes. If the declared frame length is negative or exceeds zooKeeperMaxFrameSize, the client refuses to allocate that buffer and returns this error instead. This protects the client from corrupted responses and from malicious/broken servers that would otherwise trigger huge or impossible allocations.

Source

Thrown at agents/drivers/hive-go/zookeeper_protocol.go:363

	header := make([]byte, 4)
	binary.BigEndian.PutUint32(header, uint32(len(payload)))
	if err := writeAll(client.connection, header); err != nil {
		return err
	}
	return writeAll(client.connection, payload)
}

func (client *protocolZooKeeperClient) readFrame() ([]byte, error) {
	if err := client.connection.SetReadDeadline(time.Now().Add(client.timeout)); err != nil {
		return nil, err
	}
	header := make([]byte, 4)
	if _, err := io.ReadFull(client.connection, header); err != nil {
		return nil, err
	}
	length := int(binary.BigEndian.Uint32(header))
	if length < 0 || length > zooKeeperMaxFrameSize {
		return nil, fmt.Errorf("ZooKeeper response frame is %d bytes, maximum is %d", length, zooKeeperMaxFrameSize)
	}
	payload := make([]byte, length)
	if _, err := io.ReadFull(client.connection, payload); err != nil {
		return nil, err
	}
	return payload, nil
}

func writeAll(writer io.Writer, payload []byte) error {
	for len(payload) > 0 {
		written, err := writer.Write(payload)
		if err != nil {
			return err
		}
		if written <= 0 {
			return io.ErrShortWrite
		}
		payload = payload[written:]

View on GitHub (pinned to c0390bff16)

Solutions

  1. Verify the connection actually points at a ZooKeeper server (correct host/port, no generic TCP proxy echoing garbage).
  2. Reduce the response size: narrow the query (smaller result sets, use Children instead of large getData payloads) so the reply fits under the frame cap.
  3. Check the network path (TLS termination, HAProxy) for stream corruption or truncation; enable TLS on both ends if required.
  4. If legitimately huge responses are expected, raise zooKeeperMaxFrameSize and redeploy, accepting the larger memory footprint.
  5. Re-establish the connection after the error; protocol state may be desynced and further reads will fail.

Example fix

// before
client, _ := hive.Dial("zk://10.0.0.1:9999") // wrong port: not ZooKeeper
children, err := client.Children("/a/very/large/znode")
// after
client, _ := hive.Dial("zk://10.0.0.1:2181") // correct ZooKeeper port
children, err := client.Children("/a/large/znode") // or fetch in smaller chunks
Defensive patterns

Strategy: validation

Validate before calling

// before dialing, sanity-check the endpoint is a ZooKeeper server
conn, err := net.DialTimeout("tcp", "10.0.0.1:2181", 5*time.Second)
if err != nil { log.Fatal(err) }
conn.SetDeadline(time.Now().Add(3 * time.Second))
conn.Write([]byte("ruok"))
buf := make([]byte, 8)
n, _ := conn.Read(buf)
if string(buf[:n]) != "imok" { log.Fatal("endpoint is not ZooKeeper") }
conn.Close()

Type guard

func isFrameTooLarge(err error) bool {
	return err != nil && strings.Contains(err.Error(), "maximum is")
}

Try / catch

children, err := client.Children(path)
if err != nil {
	if isFrameTooLarge(err) {
		// reconnect (stream is desynced) and fall back to a smaller query
		client.Close()
		client, err = hive.Dial(connStr)
		if err == nil {
			children, err = client.Children(shardPath(path))
		}
	}
	return fmt.Errorf("zk children: %w", err)
}

Prevention

When it happens

Trigger: Any call through request (i.e. any ZooKeeper operation via newProtocolZooKeeperClient) when the server (or an intermediary proxy) sends a frame whose 4-byte length header decodes to a value above zooKeeperMaxFrameSize or negative; also hit by readZooKeeperTestFrame when a test frame header is malformed.

Common situations: Responses larger than the library's cap (e.g. a huge getChildren or getData reply), connecting to a non-ZooKeeper service on the configured port so random bytes are interpreted as a length header, a proxy/load balancer truncating or mangling TCP streams, or protocol desync after a partial read on a reused connection.

Related errors


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