t8y2/dbx · error

ZooKeeper frame length %d is invalid

Error message

ZooKeeper frame length %d is invalid

What it means

readZooKeeperFrame reads a 4-byte big-endian length header and validates it before allocating. Lengths that are negative (as int32) or above zooKeeperMaximumFrameLen are rejected with this error, defending against absurd allocation sizes from corrupt or hostile input. The wire stream is then considered untrustworthy.

Source

Thrown at agents/drivers/zookeeper/sasl.go:160

	}
	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
	}
	return payload, nil
}

func writeZooKeeperFrame(writer io.Writer, payload []byte) error {
	if len(payload) > zooKeeperMaximumFrameLen {
		return fmt.Errorf("ZooKeeper frame length %d exceeds maximum %d", len(payload), zooKeeperMaximumFrameLen)
	}
	header := make([]byte, 4)
	binary.BigEndian.PutUint32(header, uint32(len(payload)))
	if err := writeAll(writer, header); err != nil {
		return err
	}

View on GitHub (pinned to c0390bff16)

Solutions

  1. Verify the endpoint really is a ZooKeeper server speaking the frame protocol (right port, right service).
  2. Close and reopen the connection — once framing is desynchronized the stream cannot recover.
  3. Check for code reading from the same connection outside readZooKeeperFrame, which shifts the framing offset.
  4. Ensure proxies/TLS termination are not corrupting the byte stream.

Example fix

// before
resp, _ := http.Get("http://host:2181/") // hitting ZK port with HTTP, then reading frames
// after
conn, _ := net.Dial("tcp", "zk-host:2181") // speak the binary protocol directly
Defensive patterns

Strategy: type-guard

Validate before calling

func plausibleFrameHeader(b []byte) bool {
	if len(b) < 4 { return false }
	n := int(int32(binary.BigEndian.Uint32(b)))
	return n >= 0 && n <= zooKeeperMaximumFrameLen
}

Type guard

func isFrameLengthErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "frame length") && strings.Contains(err.Error(), "is invalid")
}

Try / catch

frame, err := readZooKeeperFrame(reader)
if isFrameLengthErr(err) {
	conn.Close() // stream is desynchronized; a fresh connection is required
	return reconnectAndRedial()
}

Prevention

When it happens

Trigger: Read or zooKeeperSASLRound calls readZooKeeperFrame; the first 4 bytes of the next frame decode to an int32 that is < 0 or > zooKeeperMaximumFrameLen (e.g. reading at a misaligned offset, or garbage from a plaintext server response to a binary probe).

Common situations: Connecting to a non-ZooKeeper service on the configured port, stream desynchronization after a prior partial read, TLS/proxy corruption, or a test fake writing malformed frames (TestZooKeeperFrameValidation exercises this).

Related errors


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