{"id":"040ce56bb712021e","repo":"apache/kafka","slug":"reserved2-field-must-be-0","errorCode":null,"errorMessage":"Reserved2 field must be 0","messagePattern":"Reserved2 field must be 0","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockOutputStream.java","lineNumber":369,"sourceCode":"\n        private BD(int reserved2, int blockSizeValue, int reserved3) {\n            this.reserved2 = reserved2;\n            this.blockSizeValue = blockSizeValue;\n            this.reserved3 = reserved3;\n            validate();\n        }\n\n        public static BD fromByte(byte bd) {\n            int reserved2 = (bd >>> 0) & 15;\n            int blockMaximumSize = (bd >>> 4) & 7;\n            int reserved3 = (bd >>> 7) & 1;\n\n            return new BD(reserved2, blockMaximumSize, reserved3);\n        }\n\n        private void validate() {\n            if (reserved2 != 0) {\n                throw new RuntimeException(\"Reserved2 field must be 0\");\n            }\n            if (blockSizeValue < 4 || blockSizeValue > 7) {\n                throw new RuntimeException(\"Block size value must be between 4 and 7\");\n            }\n            if (reserved3 != 0) {\n                throw new RuntimeException(\"Reserved3 field must be 0\");\n            }\n        }\n\n        // 2^(2n+8)\n        public int getBlockMaximumSize() {\n            return 1 << ((2 * blockSizeValue) + 8);\n        }\n\n        public byte toByte() {\n            return (byte) (((reserved2 & 15) << 0) | ((blockSizeValue & 7) << 4) | ((reserved3 & 1) << 7));\n        }\n    }","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockOutputStream.java#L351-L387","documentation":"Thrown by BD.validate() when the low 4 bits (reserved2) of the LZ4 Frame Descriptor BD byte are non-zero. Per the LZ4 frame spec these bits are reserved and must be zero; BD only encodes the maximum block size in bits 4-6. A non-zero reserved2 indicates either a malformed frame or an unknown extension that this implementation refuses to read.","triggerScenarios":"BD.fromByte(b) where (b>>>0)&15 != 0, reached from readHeader() at line 128 while parsing the Frame Descriptor. Also thrown if a producer constructs BD with a non-zero reserved2 field (the public BD(int) constructor passes 0, so this only happens via the private constructor or by parsing external data).","commonSituations":"Consuming LZ4-compressed records produced by a non-Kafka encoder that sets reserved BD bits; bit-rot in the BD byte; misaligned read pointer causing the parser to read the wrong byte as BD; consuming data that has been re-encoded by an intermediary. Rare in pure Kafka deployments.","solutions":["Confirm the producer uses Kafka's CompressionType.LZ4 (always emits reserved2=0); re-encode if it does not.","Dump the bytes around the failing offset and confirm the parser is reading the BD byte at the correct position (after magic+FLG).","If the data is external LZ4, decompress it with a general LZ4 library rather than feeding it to Lz4BlockInputStream.","Re-fetch from another replica to rule out corruption of the BD byte in transit."],"exampleFix":"// before: external producer sets reserved BD bits\n//   BD byte = 0x1F  -> reserved2=0xF, blockMax=4 -> throws\n\n// after: emit BD with reserved2=0 (Kafka default)\n//   BD byte = 0x40  -> reserved2=0, blockMax=4 (64KB) -> valid\n// Achieved by always constructing BD via the public constructor:\nBD bd = new BD(BD.BLOCKSIZE_64KB); // reserved2 stays 0","handlingStrategy":"try-catch","validationCode":"// BD.reserved2 is bits 0-3 of the BD byte; must be zero.\nbyte bdByte = frameBuf.get(bdOffset);\nif ((bdByte & 0x0F) != 0) {\n    throw new IOException(\"Refusing malformed LZ4 frame: BD reserved low nibble set (0x\" + Integer.toHexString(bdByte & 0xff) + \")\");\n}","typeGuard":"null","tryCatchPattern":"try {\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(\"Reserved2 field must be 0\")) {\n        // Corrupt BD byte in LZ4 frame descriptor; treat as malformed record.\n        log.warn(\"Malformed LZ4 BD byte (reserved2 set) on {}, skipping\", tp);\n        return;\n    }\n    throw e;\n}","preventionTips":["Do not hand-construct BD bytes; only Kafka's BD(blockSize) constructor is valid, and it sets reserved bits to 0.","Pre-validate the LZ4 frame descriptor (magic, FLG, BD, HC) when ingesting from non-Kafka sources before handing off to Kafka decompression.","Keep an audit log of raw frame header bytes for batches that fail validation to identify the corrupting component."],"tags":["compression","lz4","frame-format","validation","kafka-clients"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}