{"id":"52da8c9fb2c69102","repo":"apache/kafka","slug":"unable-to-skip-exactly","errorCode":null,"errorMessage":"Unable to skip exactly","messagePattern":"Unable to skip exactly","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecord.java","lineNumber":453,"sourceCode":"    private static void skipBytes(InputStream in, int bytesToSkip) throws IOException {\n        if (bytesToSkip <= 0) return;\n\n        // Starting JDK 12, this implementation could be replaced by InputStream#skipNBytes\n        while (bytesToSkip > 0) {\n            int ns = (int) in.skip(bytesToSkip);\n            if (ns > 0 && ns <= bytesToSkip) {\n                // adjust number to skip\n                bytesToSkip -= ns;\n            } else if (ns == 0) { // no bytes skipped\n                // read one byte to check for EOS\n                if (in.read() == -1) {\n                    throw new InvalidRecordException(\"Reached end of input stream before skipping all bytes. \" +\n                        \"Remaining bytes:\" + bytesToSkip);\n                }\n                // one byte read so decrement number to skip\n                bytesToSkip--;\n            } else { // skipped negative or too many bytes\n                throw new IOException(\"Unable to skip exactly\");\n            }\n        }\n    }\n\n    private static Header[] readHeaders(ByteBuffer buffer, int numHeaders) {\n        Header[] headers = new Header[numHeaders];\n        for (int i = 0; i < numHeaders; i++) {\n            int headerKeySize = ByteUtils.readVarint(buffer);\n            if (headerKeySize < 0)\n                throw new InvalidRecordException(\"Invalid negative header key size \" + headerKeySize);\n\n            ByteBuffer headerKeyBuffer = Utils.readBytes(buffer, headerKeySize);\n\n            int headerValueSize = ByteUtils.readVarint(buffer);\n            ByteBuffer headerValue = Utils.readBytes(buffer, headerValueSize);\n\n            headers[i] = new RecordHeader(headerKeyBuffer, headerValue);\n        }","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecord.java#L435-L471","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: custom stream whose skip() may return more than requested\npublic class MyInputStream extends InputStream {\n    @Override public long skip(long n) throws IOException {\n        return in.available(); // WRONG: can exceed n\n    }\n}\n\n// after: honour the InputStream#skip contract\npublic class MyInputStream extends InputStream {\n    @Override public long skip(long n) throws IOException {\n        long skipped = 0;\n        while (skipped < n) {\n            int r = in.read();\n            if (r == -1) break;\n            skipped++;\n        }\n        return skipped;\n    }\n}","handlingStrategy":"try-catch","validationCode":"// InputStream.skip() returned negative or more than requested — a\n// misbehaving stream implementation. Wrap the stream so skip() is sane:\npublic static InputStream safeSkip(InputStream in) {\n    return new FilterInputStream(in) {\n        public long skip(long n) throws IOException {\n            long s = super.skip(n);\n            return (s < 0 || s > n) ? 0 : s; // clamp; let the loop retry\n        }\n    };\n}","typeGuard":null,"tryCatchPattern":"try {\n    PartialDefaultRecord r = DefaultRecord.readPartiallyFrom(input, baseOffset, baseTimestamp, baseSequence, logAppendTime);\n} catch (IOException e) {\n    // InputStream.skip() violated its contract\n    LOG.warn(\"Misbehaving InputStream.skip() near offset {}\", baseOffset, e);\n}","preventionTips":["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."],"tags":["kafka","record-format","input-stream","skip","streaming"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}