apache/kafka · error · RuntimeException

Reserved3 field must be 0

Error message

Reserved3 field must be 0

What it means

Thrown by BD.validate() in Lz4BlockOutputStream when the reserved3 bit (bit 7 of the BD byte) of an LZ4 frame's Block Descriptor is non-zero. The LZ4 v1.5.1 frame format reserves that bit as 0 for future use; a set bit means the frame is non-conformant. It is an unchecked RuntimeException produced while parsing the header via BD.fromByte(byte) during decompression, indicating the byte being read is not actually a valid LZ4 frame header.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockOutputStream.java:375

        }

        public static BD fromByte(byte bd) {
            int reserved2 = (bd >>> 0) & 15;
            int blockMaximumSize = (bd >>> 4) & 7;
            int reserved3 = (bd >>> 7) & 1;

            return new BD(reserved2, blockMaximumSize, reserved3);
        }

        private void validate() {
            if (reserved2 != 0) {
                throw new RuntimeException("Reserved2 field must be 0");
            }
            if (blockSizeValue < 4 || blockSizeValue > 7) {
                throw new RuntimeException("Block size value must be between 4 and 7");
            }
            if (reserved3 != 0) {
                throw new RuntimeException("Reserved3 field must be 0");
            }
        }

        // 2^(2n+8)
        public int getBlockMaximumSize() {
            return 1 << ((2 * blockSizeValue) + 8);
        }

        public byte toByte() {
            return (byte) (((reserved2 & 15) << 0) | ((blockSizeValue & 7) << 4) | ((reserved3 & 1) << 7));
        }
    }

}

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Confirm the batch was actually produced with LZ4 compression (check producer compression.type and the record batch attributes byte); a mismatch with snappy/gzip/zstd produces this.
  2. Re-fetch the batch from the broker; transient network corruption or partial reads commonly cause it.
  3. If persistent, examine the segment on the broker for corruption and verify producer/broker/client Kafka versions are compatible.
  4. Dump the raw bytes around the BD position and check frame alignment against the LZ4 frame format (magic 0x184D2204 must precede FLG/BD).
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the reserved bits of the LZ4 BD byte before handing the buffer to the decompressor.
boolean lz4BdReservedBitsClear(byte bd) {
    return (bd & 0x0F) == 0          // reserved2 (low nibble)
        && ((bd >>> 7) & 0x01) == 0; // reserved3 (high bit)
}

Try / catch

// Same shape as 320: thrown from Lz4BlockInputStream.BD.validate() on a non-zero reserved bit.
try {
    InputStream in = compression.wrapForInput(buffer, messageVersion, bufferSupplier);
    // ...read...
} catch (org.apache.kafka.common.KafkaException ke) {
    Throwable c = ke.getCause();
    if (c != null && c.getMessage() != null && c.getMessage().startsWith("Reserved")) {
        handleCorruptFrame(buffer); // skip / DLQ / advance offset
    } else {
        throw ke;
    }
}

Prevention

When it happens

Trigger: Lz4BlockInputStream (or Kafka's LZ4 decompressor invoked from MemoryRecordsBuilder/RecordsIterator) reads the BD byte from a compressed record batch and bit 7 is set. This happens when the input is not an LZ4 frame at all (e.g. garbage, snappy/gzip bytes mislabeled as LZ4), the byte stream is misaligned (offset shifted so BD is read from the wrong position), or the frame is truncated/corrupted on the wire or on disk.

Common situations: See trigger scenarios.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/dba2776247ead735.json. Report an issue: GitHub.