{"id":"da25f355aebb2e4b","repo":"apache/kafka","slug":"invalid-message-timestamp","errorCode":null,"errorMessage":"Invalid message timestamp {}","messagePattern":"Invalid message timestamp (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecordBatch.java","lineNumber":480,"sourceCode":"                                   int lastOffsetDelta,\n                                   int sizeInBytes,\n                                   byte magic,\n                                   CompressionType compressionType,\n                                   TimestampType timestampType,\n                                   long baseTimestamp,\n                                   long maxTimestamp,\n                                   long producerId,\n                                   short epoch,\n                                   int sequence,\n                                   boolean isTransactional,\n                                   boolean isControlBatch,\n                                   boolean isDeleteHorizonSet,\n                                   int partitionLeaderEpoch,\n                                   int numRecords) {\n        if (magic < RecordBatch.CURRENT_MAGIC_VALUE)\n            throw new IllegalArgumentException(\"Invalid magic value \" + magic);\n        if (baseTimestamp < 0 && baseTimestamp != NO_TIMESTAMP)\n            throw new IllegalArgumentException(\"Invalid message timestamp \" + baseTimestamp);\n\n        short attributes = computeAttributes(compressionType, timestampType, isTransactional, isControlBatch, isDeleteHorizonSet);\n\n        int position = buffer.position();\n        buffer.putLong(position + BASE_OFFSET_OFFSET, baseOffset);\n        buffer.putInt(position + LENGTH_OFFSET, sizeInBytes - LOG_OVERHEAD);\n        buffer.putInt(position + PARTITION_LEADER_EPOCH_OFFSET, partitionLeaderEpoch);\n        buffer.put(position + MAGIC_OFFSET, magic);\n        buffer.putShort(position + ATTRIBUTES_OFFSET, attributes);\n        buffer.putLong(position + BASE_TIMESTAMP_OFFSET, baseTimestamp);\n        buffer.putLong(position + MAX_TIMESTAMP_OFFSET, maxTimestamp);\n        buffer.putInt(position + LAST_OFFSET_DELTA_OFFSET, lastOffsetDelta);\n        buffer.putLong(position + PRODUCER_ID_OFFSET, producerId);\n        buffer.putShort(position + PRODUCER_EPOCH_OFFSET, epoch);\n        buffer.putInt(position + BASE_SEQUENCE_OFFSET, sequence);\n        buffer.putInt(position + RECORDS_COUNT_OFFSET, numRecords);\n        long crc = Crc32C.compute(buffer, ATTRIBUTES_OFFSET, sizeInBytes - ATTRIBUTES_OFFSET);\n        buffer.putInt(position + CRC_OFFSET, (int) crc);","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecordBatch.java#L462-L498","documentation":"Thrown by DefaultRecordBatch.writeHeader when baseTimestamp is negative and not equal to NO_TIMESTAMP (-1L). The v2 batch header reserves 8 bytes for baseTimestamp; the only permitted non-negative values are real epoch-millis timestamps, with NO_TIMESTAMP as the special 'unset' sentinel. Any other negative value indicates a caller bug. IllegalArgumentException.","triggerScenarios":"Produced when writeHeader is called with a baseTimestamp such as -2 or Long.MIN_VALUE — e.g. a producer/test computing baseTimestamp as (recordTs - earliestTs) that underflows, or code forwarding an uninitialized timestamp field. The check at DefaultRecordBatch.java:479-480 fires before any bytes are written.","commonSituations":"Tests that pass arbitrary negative numbers for timestamps; clock-skew handling code that subtracts a larger base from a smaller record timestamp; serializing a record whose timestamp was loaded from a corrupt source without sanitization; mocking frameworks that default numeric fields to negative sentinels.","solutions":["Pass a non-negative epoch-millis baseTimestamp, or RecordBatch.NO_TIMESTAMP (-1L) if you have no meaningful base time.","Guard subtractions that compute deltas: clamp the base so baseTimestamp >= 0 before calling writeHeader.","Sanitize inbound timestamps (e.g. from external systems) to either a valid millis value or NO_TIMESTAMP before producing."],"exampleFix":"// before\nlong base = firstRecordTs - clockSkewBase; // can go negative\nwriteHeader(buf, ..., base, ...);\n\n// after\nlong base = firstRecordTs - clockSkewBase;\nif (base < 0) base = RecordBatch.NO_TIMESTAMP;\nwriteHeader(buf, ..., base, ...);","handlingStrategy":"validation","validationCode":"// Timestamp is user-supplied via ProducerRecord or batch writer. Validate before send:\nlong requireValidTimestamp(long ts) {\n    if (ts < 0 && ts != RecordBatch.NO_TIMESTAMP)\n        throw new IllegalArgumentException(\"timestamp must be >= 0 or RecordBatch.NO_TIMESTAMP, got \" + ts);\n    return ts;\n}\nProducerRecord<K,V> safe(String t, K k, V v, long ts) {\n    return new ProducerRecord<>(t, null, requireValidTimestamp(ts), k, v);\n}","typeGuard":"// Box the timestamp in a value type that cannot hold an invalid value:\nstatic final class RecordTimestamp {\n    private final long ms;\n    private RecordTimestamp(long ms) { this.ms = ms; }\n    static RecordTimestamp now() { return new RecordTimestamp(System.currentTimeMillis()); }\n    static RecordTimestamp of(long in) {\n        if (in < 0 && in != RecordBatch.NO_TIMESTAMP) throw new IllegalArgumentException(\"bad ts\");\n        return new RecordTimestamp(in);\n    }\n    long millis() { return ms; }\n}","tryCatchPattern":"try {\n    producer.send(new ProducerRecord<>(topic, ts, k, v));\n} catch (IllegalArgumentException e) { // invalid timestamp\n    log.warn(\"Bad timestamp {}; sending with broker-assigned time\", ts, e);\n    producer.send(new ProducerRecord<>(topic, k, v)); // no timestamp → CREATE_TIME/broker default\n}","preventionTips":["Never derive timestamps from wall-clock arithmetic that can go negative (e.g. eventTime - large offset) without clamping at 0.","Use System.currentTimeMillis() or Instant.now().toEpochMilli() as the default; pass NO_TIMESTAMP (-1) only when you intend 'no timestamp'.","If timestamps come from upstream systems, sanitize at the ingestion boundary — a 32-bit epoch seconds value silently multiplied by 1 instead of 1000 is a classic source of bad timestamps."],"tags":["kafka","record-format","producer","timestamp","validation"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}