elastic/elasticsearch · error · LZ4Exception
Malformed input at {}
Error message
Malformed input at {} What it means
ESLZ4Decompressor's fast decompress path handles destLen==0 specially: it expects at least one source byte and that byte to be 0x00 (the LZ4 EOF marker for empty output). If srcLen is less than 1 or the first source byte is non-zero, the stream is considered malformed. This guards the zero-length output edge case before the main decode loop.
Source
Thrown at libs/lz4/src/main/java/org/elasticsearch/lz4/ESLZ4Decompressor.java:63
private ESLZ4Decompressor() {}
@Override
public int decompress(byte[] src, final int srcOff, byte[] dest, final int destOff, int destLen) {
final int srcEnd = src.length;
return decompress(src, srcOff, srcEnd - srcOff, dest, destOff, destLen);
}
private int decompress(byte[] src, final int srcOff, final int srcLen, byte[] dest, final int destOff, int destLen) {
SafeUtils.checkRange(src, srcOff, srcLen);
SafeUtils.checkRange(dest, destOff, destLen);
if (destLen == 0) {
// Allow `srcLen > 1` despite just one byte being consumed since this 'fast' decompressor does not have to fully consume the src
if (srcLen < 1 || SafeUtils.readByte(src, srcOff) != 0) {
throw new LZ4Exception("Malformed input at " + srcOff);
}
return 1;
}
final int srcEnd = srcOff + srcLen;
final int destEnd = destOff + destLen;
int sOff = srcOff;
int dOff = destOff;
while (true) {
if (sOff >= srcEnd) {
throw new LZ4Exception("Malformed input at " + sOff);
}
final int token = SafeUtils.readByte(src, sOff) & 0xFF;
++sOff;
// literalsView on GitHub (pinned to db6a809a66)
Solutions
- Verify the source was produced by the matching LZ4 fast compressor (LZ4FastDecompressor's counterpart).
- Ensure the compressed payload is not truncated; re-read or re-fetch the original bytes.
- Check that destLen matches the originally compressed length; a 0 destLen with non-empty source indicates a length bookkeeping bug upstream.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before decompressing, sanity-check the destLen==0 case
if (destLen == 0 && (srcLen < 1 || src[srcOff] != 0)) {
throw new IllegalArgumentException("Invalid LZ4 empty-output block");
} Try / catch
try {
int read = ESLZ4Decompressor.INSTANCE.decompress(src, srcOff, dest, destOff, destLen);
} catch (LZ4Exception e) {
// input is corrupt/truncated; recover from replica or snapshot
throw new IllegalStateException("Corrupt LZ4 block at offset " + srcOff, e);
} Prevention
- Always pass the exact destLen used at compression time.
- Verify source buffers are not truncated (check lengths against recorded frame sizes).
- Wrap decompression in a try/catch for LZ4Exception and trigger recovery (replica/snapshot).
When it happens
Trigger: Calling ESLZ4Decompressor.decompress (or INSTANCE.decompress) with destLen=0 but the source buffer is empty or its first byte is not the 0x00 end-of-block sentinel. Typically the result of truncating or corrupting an LZ4 frame.
Common situations: Truncated LZ4-compressed stored fields or translog entries in Elasticsearch. Passing a buffer produced by a different LZ4 variant/format. Off-by-one destLen miscalculation that yields 0 when the source is actually a real block.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Too large literalLen
- Too large matchLen
- Failed when reading jar file ${path}
- {zstdLib.getErrorName(ret)}
- database type marker not found
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/7acc1c4ba073131e.
Report an issue: GitHub.