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
- 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.
- Re-fetch the batch from the broker; transient network corruption or partial reads commonly cause it.
- If persistent, examine the segment on the broker for corruption and verify producer/broker/client Kafka versions are compatible.
- 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
- A non-zero reserved bit means the frame was produced by a non-conformant or future LZ4 implementation; treat the record as corrupt.
- Do not attempt to 'repair' the byte — discard and move on.
- Log the full frame header to detect systemic producer/version skew if it repeats.
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
- Block size value must be between 4 and 7
- Block checksum mismatch
- lz4 doesn't support given compression level: level
- Stream ended prematurely
- Stream unsupported (invalid magic bytes)
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/dba2776247ead735.json.
Report an issue: GitHub.