apache/kafka · error · InvalidRecordException

Found invalid record structure

Error message

Found invalid record structure

What it means

A catch-all InvalidRecordException wrapping a BufferUnderflowException or IllegalArgumentException raised while reading the per-record fields from a ByteBuffer. It signals that the parser ran off the end of the buffer or hit an invalid varint while decoding attributes, timestampDelta, offsetDelta, key, value, or headers, but no specific guard caught it first.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecord.java:358

                throw new InvalidRecordException("Found invalid number of record headers " + numHeaders);
            if (numHeaders > buffer.remaining())
                throw new InvalidRecordException("Found invalid number of record headers. " + numHeaders + " is larger than the remaining size of the buffer");

            final Header[] headers;
            if (numHeaders == 0)
                headers = Record.EMPTY_HEADERS;
            else
                headers = readHeaders(buffer, numHeaders);

            // validate whether we have read all header bytes in the current record
            if (buffer.position() - recordStart != sizeOfBodyInBytes)
                throw new InvalidRecordException("Invalid record size: expected to read " + sizeOfBodyInBytes +
                        " bytes in record payload, but instead read " + (buffer.position() - recordStart));

            int totalSizeInBytes = ByteUtils.sizeOfVarint(sizeOfBodyInBytes) + sizeOfBodyInBytes;
            return new DefaultRecord(totalSizeInBytes, attributes, offset, timestamp, sequence, key, value, headers);
        } catch (BufferUnderflowException | IllegalArgumentException e) {
            throw new InvalidRecordException("Found invalid record structure", e);
        }
    }

    public static PartialDefaultRecord readPartiallyFrom(InputStream input,
                                                         long baseOffset,
                                                         long baseTimestamp,
                                                         int baseSequence,
                                                         Long logAppendTime) throws IOException {
        int sizeOfBodyInBytes = ByteUtils.readVarint(input);
        int totalSizeInBytes = ByteUtils.sizeOfVarint(sizeOfBodyInBytes) + sizeOfBodyInBytes;

        return readPartiallyFrom(input, totalSizeInBytes, baseOffset, baseTimestamp,
            baseSequence, logAppendTime);
    }

    private static PartialDefaultRecord readPartiallyFrom(InputStream input,
                                                          int sizeInBytes,
                                                          long baseOffset,

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Check the broker and client logs for preceding CRC or fetch-size errors on the same partition; raise fetch.message.maxBytes if batches are being truncated in flight.
  2. Confirm the buffer handed to DefaultRecord.readFrom is the one returned by MemoryRecords / DefaultRecordBatch and has not been re-sliced by application code.
  3. Run kafka-dump-log on the source segment; if it fails the same way the record is corrupt on disk and must be recovered from a replica.
  4. Verify client and broker are on mutually supported versions and both use magic-v2 batches (Apache Kafka 0.11+).
Defensive patterns

Strategy: try-catch

Validate before calling

// Generic corruption (BufferUnderflowException / IllegalArgumentException
// while parsing). No single pre-check; validate the whole slice is well-formed
// by attempting a strict parse on a duplicated, slice()ed buffer first.

Type guard

// Cheap structural sanity check before delegating.
private static boolean looksLikeValidRecord(ByteBuffer b, int sizeOfBodyInBytes) {
    return b != null && b.remaining() >= sizeOfBodyInBytes && sizeOfBodyInBytes > 0;
}

Try / catch

try {
    DefaultRecord r = DefaultRecord.readFrom(buffer, baseOffset, baseTimestamp, baseSequence, logAppendTime);
} catch (InvalidRecordException e) {
    // wraps BufferUnderflowException or IllegalArgumentException
    LOG.warn("Corrupt record structure near offset {}", baseOffset, e);
}

Prevention

When it happens

Trigger: Raised at DefaultRecord.java:357-359 (the try/catch around the body of the private readFrom(ByteBuffer, ...)). Triggered by buffer underflow when Utils.readBytes asks for more key/value bytes than remain, by an illegal-argument from ByteUtils when a varint overflows, or by any other RuntimeException from the field-decoding helpers inside the try block.

Common situations: Truncated fetch response (network cut, broker returned a partial batch), disk/page-cache corruption, an off-spec producer, or a custom consumer that re-slices MemoryRecords incorrectly before iterating. Frequently co-occurs with checksum failures in the broker log.

Related errors


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