apache/kafka · warning · IOException
Unable to skip exactly
Error message
Unable to skip exactly
What it means
Thrown by skipBytes when InputStream.skip() returns a value that is either negative or larger than the requested skip amount. This is a violation of the InputStream#skip contract by the underlying stream implementation, not corrupt record data; the library cannot make progress safely and refuses to continue.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecord.java:453
private static void skipBytes(InputStream in, int bytesToSkip) throws IOException {
if (bytesToSkip <= 0) return;
// Starting JDK 12, this implementation could be replaced by InputStream#skipNBytes
while (bytesToSkip > 0) {
int ns = (int) in.skip(bytesToSkip);
if (ns > 0 && ns <= bytesToSkip) {
// adjust number to skip
bytesToSkip -= ns;
} else if (ns == 0) { // no bytes skipped
// read one byte to check for EOS
if (in.read() == -1) {
throw new InvalidRecordException("Reached end of input stream before skipping all bytes. " +
"Remaining bytes:" + bytesToSkip);
}
// one byte read so decrement number to skip
bytesToSkip--;
} else { // skipped negative or too many bytes
throw new IOException("Unable to skip exactly");
}
}
}
private static Header[] readHeaders(ByteBuffer buffer, int numHeaders) {
Header[] headers = new Header[numHeaders];
for (int i = 0; i < numHeaders; i++) {
int headerKeySize = ByteUtils.readVarint(buffer);
if (headerKeySize < 0)
throw new InvalidRecordException("Invalid negative header key size " + headerKeySize);
ByteBuffer headerKeyBuffer = Utils.readBytes(buffer, headerKeySize);
int headerValueSize = ByteUtils.readVarint(buffer);
ByteBuffer headerValue = Utils.readBytes(buffer, headerValueSize);
headers[i] = new RecordHeader(headerKeyBuffer, headerValue);
}View on GitHub (pinned to c31c9215e1)
Solutions
- Identify the concrete InputStream subclass passed to readPartiallyFrom and audit its skip(long) implementation against the java.io.InputStream contract (0 <= returned <= n).
- If you control the stream, fix skip() to return only the bytes actually skipped, or wrap it in a conformant adapter that loops on read() when skip() misbehaves.
- If the bug is in a compression codec (snappy/lz4/zstd), upgrade the codec library to a version with a spec-compliant skip().
- As a workaround, switch the affected path to the ByteBuffer readFrom overload which does not rely on InputStream#skip.
Example fix
// before: custom stream whose skip() may return more than requested
public class MyInputStream extends InputStream {
@Override public long skip(long n) throws IOException {
return in.available(); // WRONG: can exceed n
}
}
// after: honour the InputStream#skip contract
public class MyInputStream extends InputStream {
@Override public long skip(long n) throws IOException {
long skipped = 0;
while (skipped < n) {
int r = in.read();
if (r == -1) break;
skipped++;
}
return skipped;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// InputStream.skip() returned negative or more than requested — a
// misbehaving stream implementation. Wrap the stream so skip() is sane:
public static InputStream safeSkip(InputStream in) {
return new FilterInputStream(in) {
public long skip(long n) throws IOException {
long s = super.skip(n);
return (s < 0 || s > n) ? 0 : s; // clamp; let the loop retry
}
};
} Try / catch
try {
PartialDefaultRecord r = DefaultRecord.readPartiallyFrom(input, baseOffset, baseTimestamp, baseSequence, logAppendTime);
} catch (IOException e) {
// InputStream.skip() violated its contract
LOG.warn("Misbehaving InputStream.skip() near offset {}", baseOffset, e);
} Prevention
- Prefer standard InputStream implementations (ByteArrayInputStream, FileInputStream) whose skip() honors its contract; beware custom decompressing streams.
- On JDK 12+, prefer InputStream.skipNBytes which is well-defined; if you must wrap, normalize skip() results.
- Always catch IOException (not only InvalidRecordException) when reading from InputStream-backed records.
When it happens
Trigger: Raised at DefaultRecord.java:452-454 in skipBytes(InputStream, int) when InputStream.skip(n) returns ns < 0 or ns > n. Triggered by a custom or wrapped InputStream (for example a decompression stream, a buggy network buffer, or a third-party InputFilterStream) whose skip(N) over-reports or under-reports the bytes consumed.
Common situations: Custom InputStream passed into a record-deserialization path (e.g. a decompressing wrapper around a compressed batch), a JDBC/byte-array adapter whose skip() is incorrectly implemented, an old or buggy JDK stream implementation, or a third-party compression library (snappy/lz4/zstd) with a non-conformant skip.
Related errors
- Reached end of input stream before skipping all bytes. Remai
- Invalid record size: expected {} bytes in record payload, bu
- Found invalid number of record headers {}
- Found invalid number of record headers. {} is larger than th
- Invalid record size: expected to read {} bytes in record pay
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/52da8c9fb2c69102.json.
Report an issue: GitHub.