apache/kafka · warning · RuntimeException
mark not supported
Error message
mark not supported
What it means
mark(int) is overridden in Lz4BlockInputStream to unconditionally throw RuntimeException because the stream consumes and decompresses from a forward-only ByteBuffer and cannot snapshot a rewind point. This is a deliberate API contract: the class does not support mark/reset semantics inherited from java.io.InputStream.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/compress/Lz4BlockInputStream.java:271
}
int skipped = (int) Math.min(n, available());
decompressedBuffer.position(decompressedBuffer.position() + skipped);
return skipped;
}
@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);View on GitHub (pinned to c31c9215e1)
Solutions
- Remove the mark() call — Lz4BlockInputStream is forward-only by design; buffer the decompressed bytes upstream if you need re-read.
- If re-read is genuinely required, fully drain the stream into a ByteArrayOutputStream / ByteBuffer first, then replay that buffer.
- Guard callers with an explicit markSupported() check (it inherits false from InputStream) before invoking mark().
Example fix
// before
InputStream decompressed = new Lz4BlockInputStream(buf, supplier, true);
decompressed.mark(1024);
...
decompressed.reset();
// after: buffer the whole frame if you need re-read
ByteArrayOutputStream drained = new ByteArrayOutputStream();
try (InputStream in = new Lz4BlockInputStream(buf, supplier, true)) {
in.transferTo(drained);
}
byte[] replayable = drained.toByteArray(); Defensive patterns
Strategy: validation
Validate before calling
// Lz4BlockInputStream.markSupported() is hardcoded false.
// Guard every call site that reaches for mark().
InputStream in = ...;
if (in.markSupported()) {
in.mark(readlimit);
} else {
// buffer externally, or re-open the source
} Type guard
// Narrow on the capability, not the class — works for any non-markable stream.
static boolean canMark(InputStream in) {
return in != null && in.markSupported();
} Try / catch
try {
in.mark(readlimit);
} catch (RuntimeException e) {
if ("mark not supported".equals(e.getMessage())) {
// Fall back to buffered read-into-byte[] and replay from memory.
} else throw e;
} Prevention
- Never call mark() on a stream whose contract forbids it; Lz4BlockInputStream documents mark/reset as unsupported.
- If you need replay semantics, wrap the decompressed bytes in a ByteArrayInputStream or PushbackInputStream after fully draining the LZ4 stream.
- Prefer try-with-resources and a single forward-only pass over decompressed records — Kafka record batches are designed for sequential consumption.
- Lint for mark()/reset() calls on InputStream references of unknown provenance; gate them behind a markSupported() check.
When it happens
Trigger: Any code calling Lz4BlockInputStream.mark(readLimit) — typically a wrapper utility, a third-party library (e.g. some IOHelpers, Apache Commons IO, certain serialization frameworks), or generated code that probes markSupported() incorrectly and then calls mark(). Triggered at line 271.
Common situations: Using a generic InputStream wrapper that assumes mark/reset (e.g. some XML/JSON parsers, java.io.BufferedInputStream-style helpers) on top of a Kafka LZ4 stream; old code migrated from a different decompressor that did support mark; testing utilities that mark every stream defensively. Less common with direct Kafka consumers, more common in Connect transforms or Streams custom serializers.
Related errors
- reset not supported
- The stream is already closed
- Stream ended prematurely
- Stream unsupported (invalid magic bytes)
- Stream frame descriptor corrupted
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/32fdce6c90a8008a.json.
Report an issue: GitHub.