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.
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)}
- AssignProcessToJobObject
- Cannot remove version
- checksum mismatch, expected
- Could not close systemd socket
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/e10941eac70d07e5.
Report an issue: GitHub.
Appendix: 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)