apache/kafka · error · IllegalArgumentException
Invalid message timestamp {}
Error message
Invalid message timestamp {} What it means
Thrown by LegacyRecord.write() when serializing a legacy (v0/v1) message: a record timestamp must be either non-negative or exactly RecordBatch.NO_TIMESTAMP (the sentinel -1 that means 'no timestamp'). Any other negative value is rejected because the on-wire timestamp field is an unsigned 8-byte value and negative numbers are not representable/valid. The guard protects against writing corrupt frames that would later fail CRC or parsing.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/LegacyRecord.java:449
long timestamp,
byte[] key,
byte[] value) throws IOException {
write(out, magic, crc, attributes, timestamp, wrapNullable(key), wrapNullable(value));
}
// Write a record to the buffer, if the record's compression type is none, then
// its value payload should be already compressed with the specified type
private static void write(DataOutputStream out,
byte magic,
long crc,
byte attributes,
long timestamp,
ByteBuffer key,
ByteBuffer value) throws IOException {
if (magic != RecordBatch.MAGIC_VALUE_V0 && magic != RecordBatch.MAGIC_VALUE_V1)
throw new IllegalArgumentException("Invalid magic value " + magic);
if (timestamp < 0 && timestamp != RecordBatch.NO_TIMESTAMP)
throw new IllegalArgumentException("Invalid message timestamp " + timestamp);
// write crc
out.writeInt((int) (crc & 0xffffffffL));
// write magic value
out.writeByte(magic);
// write attributes
out.writeByte(attributes);
// maybe write timestamp
if (magic > RecordBatch.MAGIC_VALUE_V0)
out.writeLong(timestamp);
// write the key
if (key == null) {
out.writeInt(-1);
} else {
int size = key.remaining();
out.writeInt(size);View on GitHub (pinned to c31c9215e1)
Solutions
- Set the timestamp to RecordBatch.NO_TIMESTAMP (-1L) when you mean 'no timestamp', not any other negative number.
- Ensure any timestamp arithmetic cannot produce a negative result; clamp to >=0 or NO_TIMESTAMP.
- If forwarding timestamps from an upstream schema, map its null/missing value to RecordBatch.NO_TIMESTAMP before calling LegacyRecord.write.
Example fix
// before LegacyRecord.write(out, magic, crc, attributes, userTimestamp - offset, key, value); // after long ts = userTimestamp == null ? RecordBatch.NO_TIMESTAMP : Math.max(0, userTimestamp - offset); LegacyRecord.write(out, magic, crc, attributes, ts, key, value);
Defensive patterns
Strategy: validation
Validate before calling
// Validate timestamp before serializing a LegacyRecord (magic v1).
// RecordBatch.NO_TIMESTAMP (-1) is the only allowed non-positive value.
long NO_TIMESTAMP = -1L;
if (timestamp < 0L && timestamp != NO_TIMESTAMP) {
throw new IllegalArgumentException(
"Rejecting record: timestamp " + timestamp + " must be >= 0 or NO_TIMESTAMP(-1)");
} Type guard
// Timestamp must be a non-negative long, or the sentinel NO_TIMESTAMP(-1).
static boolean isValidLegacyTimestamp(long ts) {
return ts >= 0L || ts == -1L;
} Try / catch
try {
// ... call API that serializes a LegacyRecord (e.g. MemoryRecordsBuilder.append) ...
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Invalid message timestamp")) {
// Drop / route-to-DLT the offending record; never silently retry with same ts.
throw new RecordSerializationException("Bad producer timestamp", e);
}
throw e;
} Prevention
- Always set producer record timestamps via System.currentTimeMillis() or let the producer default to CREATE_TIME instead of injecting raw/external values.
- Treat 0 as the floor for real timestamps; reserve -1 (RecordBatch.NO_TIMESTAMP) only when you genuinely mean 'no timestamp'.
- If ingesting timestamps from an upstream source (DB row, external event), sanitize/clamp them in a Serializer or ProducerInterceptor before send().
- Add a unit test asserting that negative non-sentinel timestamps are rejected before reaching the wire.
When it happens
Trigger: Calling any of the LegacyRecord.write(...) overloads (or the higher-level MemoryRecordsBuilder path that targets magic=0/1) with a timestamp such as -2, Long.MIN_VALUE, or any negative value other than -1. Most commonly reached by tests or custom serializers that hand-craft records, or by mis-mapping a 'missing timestamp' sentinel to something other than NO_TIMESTAMP before writing.
Common situations: Reusing a sentinel constant (e.g. -1 from a different schema, or 0 interpreted as 'unset' then decremented), off-by-one timestamp math, arithmetic underflow when subtracting offsets, or a producer/serializer library that represents 'no timestamp' as a different negative value than Kafka's NO_TIMESTAMP.
Related errors
- Timestamp type must be provided to compute attributes for me
- Invalid magic used in LegacyRecord: {}
- The target time for partition {} is {}. The target time cann
- The timeout cannot be negative.
- Topic partitions collection to assign to cannot be null
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/2d0fad9e4fc708bb.json.
Report an issue: GitHub.