{"id":"8fd84772c9afbe5d","repo":"apache/kafka","slug":"reserved-bits-must-be-0","errorCode":null,"errorMessage":"Reserved bits must be 0","messagePattern":"Reserved bits must be 0","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockOutputStream.java","lineNumber":315,"sourceCode":"            int blockIndependence = (flg >>> 5) & 1;\n            int version = (flg >>> 6) & 3;\n\n            return new FLG(reserved,\n                           contentChecksum,\n                           contentSize,\n                           blockChecksum,\n                           blockIndependence,\n                           version);\n        }\n\n        public byte toByte() {\n            return (byte) (((reserved & 3) << 0) | ((contentChecksum & 1) << 2)\n                    | ((contentSize & 1) << 3) | ((blockChecksum & 1) << 4) | ((blockIndependence & 1) << 5) | ((version & 3) << 6));\n        }\n\n        private void validate() {\n            if (reserved != 0) {\n                throw new RuntimeException(\"Reserved bits must be 0\");\n            }\n            if (blockIndependence != 1) {\n                throw new RuntimeException(\"Dependent block stream is unsupported\");\n            }\n            if (version != VERSION) {\n                throw new RuntimeException(String.format(\"Version %d is unsupported\", version));\n            }\n        }\n\n        public boolean isContentChecksumSet() {\n            return contentChecksum == 1;\n        }\n\n        public boolean isContentSizeSet() {\n            return contentSize == 1;\n        }\n\n        public boolean isBlockChecksumSet() {","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockOutputStream.java#L297-L333","documentation":"Thrown by FLG.validate() (invoked from the FLG constructor / FLG.fromByte) when the two low-order reserved bits of the LZ4 Frame Descriptor FLG byte are non-zero. Per the LZ4 frame spec these bits MUST be zero for forward compatibility; a non-zero value means the frame was produced by an unknown/extension format that this implementation refuses to interpret.","triggerScenarios":"FLG.fromByte(b) parsing a Frame Descriptor whose bits 0-1 are set, e.g. during Lz4BlockInputStream.readHeader() at line 127 while decoding a frame produced by a non-Kafka LZ4 implementation (or by a future/extended LZ4 format). Also reachable if a producer manually constructs FLG with reserved != 0.","commonSituations":"Consuming LZ4-compressed Kafka records produced by a third-party client that sets reserved FLG bits; bit-rot in the FLG byte; future LZ4 frame version that defines new flag bits in the reserved range; testing with hand-crafted LZ4 frames; reading Kafka data through a tool that rewrites headers.","solutions":["Confirm the data was produced by a Kafka client (which always emits reserved=0); if not, re-encode with Kafka's CompressionType.LZ4.","Inspect the raw FLG byte at the failing offset and compare against the LZ4 frame spec to identify which extension set the reserved bits.","If the producer is yours, ensure FLG is constructed only via the public FLG(boolean blockChecksum) constructor (reserved defaults to 0).","Re-fetch from another replica to rule out in-flight corruption of the FLG byte."],"exampleFix":"// before: hand-built FLG with a custom reserved bit\nFLG flg = new FLG(1, 0, 0, 1, 1, 1); // reserved=1 -> throws\n\n// after: use the public constructor, reserved stays 0\nFLG flg = new FLG(true); // blockChecksum=true, all reserved bits 0","handlingStrategy":"try-catch","validationCode":"// FLG reserved bits are bits 0-1 of the FLG byte; they must be zero.\n// Pre-validate a raw LZ4 frame before constructing FLG.fromByte().\nbyte flgByte = frameBuf.get(flgOffset);\nif ((flgByte & 0x03) != 0) {\n    throw new IOException(\"Refusing malformed LZ4 frame: FLG reserved bits set (0x\" + Integer.toHexString(flgByte & 0xff) + \")\");\n}","typeGuard":"null","tryCatchPattern":"try {\n    // FLG.fromByte is reached indirectly through new Lz4BlockInputStream(...)\n    try (Lz4BlockInputStream in = new Lz4BlockInputStream(payload, BufferSupplier.NO_CACHING, false)) {\n        // consume\n    }\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Reserved bits must be 0\")) {\n        // Malformed LZ4 frame header — quarantine the record and continue.\n        log.warn(\"Discarding malformed LZ4 frame (reserved bits set) on {}\", tp);\n        return;\n    }\n    throw e;\n}","preventionTips":["This path is only reachable when decompressing externally-supplied LZ4 frames (i.e. corrupted records or data not produced by Kafka); never attempt to construct FLG by hand.","When ingesting LZ4 data from non-Kafka sources, validate the frame header (magic + FLG + BD + HC) before passing to Kafka's decompressor.","Keep producer and consumer Kafka client versions aligned so FLG bytes are always produced by validated code.","Log the raw FLG byte on failure to speed root-cause of silent corruption."],"tags":["compression","lz4","frame-format","validation","kafka-clients"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}