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.

Solutions

  1. Verify the source was produced by the matching LZ4 fast compressor (LZ4FastDecompressor's counterpart).
  2. Ensure the compressed payload is not truncated; re-read or re-fetch the original bytes.
  3. 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

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

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/7acc1c4ba073131e. Report an issue: GitHub.

Appendix: 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;

            // literals

View on GitHub (pinned to db6a809a66)