elastic/elasticsearch · error · IllegalArgumentException
{zstdLib.getErrorName(ret)}
Error message
{zstdLib.getErrorName(ret)} What it means
Thrown as IllegalArgumentException when libzstd's ZSTD_decompress returns an error code (detected via ZSTD_isError). The message is the human-readable error name from ZSTD_getErrorName(ret). Common libzstd errors include corrupted frame magic, truncated input, destination buffer too small, or checksum mismatch. This indicates the compressed data is invalid or the output buffer is undersized.
Source
Thrown at libs/native/src/main/java/org/elasticsearch/nativeaccess/Zstd.java:77
throw new IllegalArgumentException(zstdLib.getErrorName(ret));
} else if (ret < 0 || ret > Integer.MAX_VALUE) {
throw new IllegalStateException("Integer overflow? ret=" + ret);
}
return (int) ret;
}
/**
* Decompress the content of {@code src} into {@code dst}, and return the number of decompressed bytes. {@link ByteBuffer#position()}
* and {@link ByteBuffer#limit()} of both {@link ByteBuffer}s are left unmodified.
*/
public int decompress(CloseableByteBuffer dst, CloseableByteBuffer src) {
Objects.requireNonNull(dst, "Null destination buffer");
Objects.requireNonNull(src, "Null source buffer");
long dstSize = dst.buffer().remaining();
long srcSize = src.buffer().remaining();
long ret = zstdLib.decompress(MemorySegment.ofBuffer(dst.buffer()), dstSize, MemorySegment.ofBuffer(src.buffer()), srcSize);
if (zstdLib.isError(ret)) {
throw new IllegalArgumentException(zstdLib.getErrorName(ret));
} else if (ret < 0 || ret > Integer.MAX_VALUE) {
throw new IllegalStateException("Integer overflow? ret=" + ret);
}
return (int) ret;
}
/**
* Decompress the content of {@code src} into {@code dst}, and return the number of decompressed bytes.
* Both segments may be native or heap-backed. On JDK 22+ heap segments are passed through the
* critical downcall without copying; on JDK 21 the fallback adapter stages them via a confined arena.
*/
public int decompress(MemorySegment dst, MemorySegment src) {
Objects.requireNonNull(dst, "Null dst segment");
Objects.requireNonNull(src, "Null src segment");
long ret = zstdLib.decompressHeap(dst, dst.byteSize(), src, src.byteSize());
if (zstdLib.isError(ret)) {
throw new IllegalArgumentException(zstdLib.getErrorName(ret));
} else if (ret < 0 || ret > Integer.MAX_VALUE) {View on GitHub (pinned to db6a809a66)
Solutions
- Verify the source buffer starts with the zstd magic number (0x28B52FFD) and spans a complete frame.
- Ensure dst.remaining() >= the original uncompressed size (use the frame's content size header or compressBound).
- If reading from a stream/channel, verify all compressed bytes were read before decompressing.
- Check for disk corruption: re-read or restore the compressed data from a healthy replica.
- Log the exact ZSTD_getErrorName string to distinguish corruption from buffer-size issues.
Example fix
// before: dst buffer too small ByteBuffer dst = ByteBuffer.allocate(src.remaining()); // sized for compressed, not decompressed zstd.decompress(wrap(dst), wrap(src)); // after: dst sized to decompressed content size long decompressedSize = Zstd.decompressedSize(src); // read frame header ByteBuffer dst = ByteBuffer.allocate((int) decompressedSize); zstd.decompress(wrap(dst), wrap(src));
Defensive patterns
Strategy: validation
Validate before calling
// Verify zstd magic bytes and minimum frame size before decompressing
int magic = src.getInt(src.position()) & 0xFFFFFFFF;
if (magic != 0xFD2FB528) {
throw new IllegalArgumentException("Source buffer does not start with zstd magic number");
}
if (dst.remaining() < minRequiredDecompressedSize) {
throw new IllegalArgumentException("Destination buffer too small for decompressed data");
} Try / catch
try {
int decompressed = zstd.decompress(dst, src);
} catch (IllegalArgumentException e) {
logger.error("Zstd decompression failed: {}", e.getMessage());
// trigger segment recovery or fallback read
throw new IOException("Corrupted zstd-compressed data", e);
} Prevention
- Always size the destination buffer from the frame's declared content size, not the compressed size.
- Validate the zstd magic number before decompressing untrusted input.
- Ensure complete reads of compressed data before invoking decompress.
- Monitor disk health to catch corruption before it reaches the decompressor.
When it happens
Trigger: Calling Zstd.decompress(dst, src) where src does not contain a valid complete zstd frame, or dst.remaining() is smaller than the decompressed content size. Also triggered by partial reads where only a prefix of the compressed frame was loaded into src.
Common situations: Reading a corrupted or truncated Lucene segment compressed with zstd. Passing a ByteBuffer whose position/limit slice does not cover the full compressed frame. Mixing zstd frame versions. Disk I/O errors causing partial reads. Using a dst buffer sized for the compressed rather than decompressed size.
Related errors
- {zstdLib.getErrorName(hint)}
- Incorrect format for line [%s]
- Release [%s] already recorded with version id [%s], cannot u
- Failed to read branches.json from {path}
- Duplicate version constants {v1}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/e10941eac70d07e5.
Report an issue: GitHub.