apache/kafka · error · IllegalArgumentException

Invalid timestamp: %d. Timestamp should always be non-negati

Error message

Invalid timestamp: %d. Timestamp should always be non-negative or null.

What it means

Thrown by the ProducerRecord constructor when a non-null timestamp is negative. The producer accepts either null (it will stamp the record with current time) or a non-negative millisecond epoch value; a negative value would produce an invalid record timestamp and is rejected at construction with an IllegalArgumentException.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/producer/ProducerRecord.java:75

    private final V value;
    private final Long timestamp;

    /**
     * Creates a record with a specified timestamp to be sent to a specified topic and partition
     * 
     * @param topic The topic the record will be appended to
     * @param partition The partition to which the record should be sent
     * @param timestamp The timestamp of the record, in milliseconds since epoch. If null, the producer will assign
     *                  the timestamp using System.currentTimeMillis().
     * @param key The key that will be included in the record
     * @param value The record contents
     * @param headers the headers that will be included in the record
     */
    public ProducerRecord(String topic, Integer partition, Long timestamp, K key, V value, Iterable<Header> headers) {
        if (topic == null)
            throw new IllegalArgumentException("Topic cannot be null.");
        if (timestamp != null && timestamp < 0)
            throw new IllegalArgumentException(
                    String.format("Invalid timestamp: %d. Timestamp should always be non-negative or null.", timestamp));
        if (partition != null && partition < 0)
            throw new IllegalArgumentException(
                    String.format("Invalid partition: %d. Partition number should always be non-negative or null.", partition));
        this.topic = topic;
        this.partition = partition;
        this.key = key;
        this.value = value;
        this.timestamp = timestamp;
        this.headers = new RecordHeaders(headers);
    }

    /**
     * Creates a record with a specified timestamp to be sent to a specified topic and partition
     *
     * @param topic The topic the record will be appended to
     * @param partition The partition to which the record should be sent
     * @param timestamp The timestamp of the record, in milliseconds since epoch. If null, the producer will assign the

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Pass null instead of a negative sentinel when you have no timestamp; the producer will assign System.currentTimeMillis() for you.
  2. Sanitize upstream timestamps before construction: long ts = sourceTs != null && sourceTs >= 0 ? sourceTs : null; then build the ProducerRecord.
  3. If using a custom/mock clock in tests, ensure it returns a non-negative epoch millis value, or inject null for the timestamp in test fixtures.

Example fix

// before
long ts = event.getEventTime(); // returns -1 when unknown
new ProducerRecord<>(topic, partition, ts, key, value);

// after
Long ts = event.getEventTime();
if (ts != null && ts < 0) ts = null;
new ProducerRecord<>(topic, partition, ts, key, value);
Defensive patterns

Strategy: validation

Validate before calling

// Timestamp must be null or >= 0 (ms since epoch).
Long timestamp = /* source timestamp */;
if (timestamp != null && timestamp < 0L) {
    throw new IllegalArgumentException("timestamp must be non-negative or null, got " + timestamp);
}
ProducerRecord<K, V> record = new ProducerRecord<>(topic, partition, timestamp, key, value);

Type guard

// Narrows a Long to a valid producer timestamp (null OR non-negative).
static Long requireValidTimestamp(Long ts) {
    if (ts != null && ts < 0L) throw new IllegalArgumentException("negative timestamp: " + ts);
    return ts;
}

Try / catch

try {
    ProducerRecord<K, V> r = new ProducerRecord<>(topic, partition, timestamp, key, value);
    producer.send(r);
} catch (IllegalArgumentException e) {
    // Either drop the record or re-stamp with System.currentTimeMillis() and retry build
}

Prevention

When it happens

Trigger: Calling any ProducerRecord constructor that takes a Long timestamp with a negative value, e.g. new ProducerRecord<>(topic, partition, -1L, key, value). Also when the timestamp is derived from System.currentTimeMillis() under a clock skew or mock clock returning negative values, or from a domain field that was never validated and was set to -1 as a sentinel.

Common situations: Using -1L or 0L-minus-one as a 'no timestamp' sentinel instead of null; consuming a timestamp from an upstream system (e.g. CDC, database column) that uses 0 or negative for missing values; testing with a mocked Time/ Clock that returns negative millis; clock skew on misconfigured VMs or containers where System.currentTimeMillis() is unreliable.

Related errors


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