apache/kafka · warning · RuntimeException

reset not supported

Error message

reset not supported

What it means

reset() is overridden in Lz4BlockInputStream to unconditionally throw RuntimeException because the underlying ByteBuffer has already been advanced past the decompressed bytes and there is no stored mark to return to. Pairs with mark(int) (error 312) — the class declares neither operation is supported.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockInputStream.java:276

    @Override
    public int available() {
        return decompressedBuffer == null ? 0 : decompressedBuffer.remaining();
    }

    @Override
    public void close() {
        bufferSupplier.release(decompressionBuffer);
    }

    @Override
    public void mark(int readlimit) {
        throw new RuntimeException("mark not supported");
    }

    @Override
    public void reset() {
        throw new RuntimeException("reset not supported");
    }

    /**
     * Checks whether the version of lz4 on the classpath has the fix for reading from ByteBuffers with
     * non-zero array offsets (see https://github.com/lz4/lz4-java/pull/65)
     */
    static void detectBrokenLz4Version() {
        byte[] source = new byte[]{1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3};
        final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();

        final byte[] compressed = new byte[compressor.maxCompressedLength(source.length)];
        final int compressedLength = compressor.compress(source, 0, source.length, compressed, 0,
                                                         compressed.length);

        // allocate an array-backed ByteBuffer with non-zero array-offset containing the compressed data
        // a buggy decompressor will read the data from the beginning of the underlying array instead of
        // the beginning of the ByteBuffer, failing to decompress the invalid data.
        final byte[] zeroes = {0, 0, 0, 0, 0};

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Eliminate the reset() call; Lz4BlockInputStream is single-pass.
  2. Buffer the decompressed output into a byte[] or ByteBuffer upstream and replay from there.
  3. Verify the calling library honors markSupported()==false before invoking reset().

Example fix

// before
InputStream in = new Lz4BlockInputStream(buf, supplier, true);
in.mark(0);
readHeader(in);
in.reset();

// after
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
try (InputStream in = new Lz4BlockInputStream(buf, supplier, true)) {
    in.transferTo(tmp);
}
byte[] all = tmp.toByteArray(); // replay as many times as needed
Defensive patterns

Strategy: validation

Validate before calling

// Symmetric to mark(): reset() is only valid when markSupported() is true.
InputStream in = ...;
if (in.markSupported()) {
    in.reset();
} else {
    throw new IllegalStateException("Cannot reset non-bufferable LZ4 stream; re-open the source");
}

Type guard

static boolean canReset(InputStream in) {
    return in != null && in.markSupported(); // reset capability == mark capability in java.io
}

Try / catch

try {
    in.reset();
} catch (RuntimeException e) {
    if ("reset not supported".equals(e.getMessage())) {
        // re-open the LZ4 stream from the original ByteBuffer instead of resetting.
    } else throw e;
}

Prevention

When it happens

Trigger: Any invocation of Lz4BlockInputStream.reset(), e.g. from a parser or wrapper that previously called mark() and now attempts to rewind. Reached at line 276. Often thrown right after error 312 if a caller ignores the mark() failure.

Common situations: Same shape as error 312: third-party IO/serialization libraries that assume mark/reset; migrating code from a resettable stream; defensive reset() calls in cleanup paths. Indicates the caller violated the documented InputStream contract for this class.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/0d2cf3831618f1df.json. Report an issue: GitHub.