apache/kafka · error · IllegalArgumentException

The target time for partition {} is {}. The target time cann

Error message

The target time for partition {} is {}. The target time cannot be negative.

What it means

Thrown by KafkaConsumer.offsetsForTimes(Map, Duration) when any searched timestamp is negative. The API treats timestamps as non-negative epoch millis (earliest/latest sentinels are handled separately), so a negative value is rejected as a client-side precondition violation before any broker request.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java:1024

        } finally {
            release();
        }
    }

    @Override
    public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch) {
        return offsetsForTimes(timestampsToSearch, Duration.ofMillis(defaultApiTimeoutMs));
    }

    @Override
    public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch, Duration timeout) {
        acquireAndEnsureOpen();
        try {
            for (Map.Entry<TopicPartition, Long> entry : timestampsToSearch.entrySet()) {
                // we explicitly exclude the earliest and latest offset here so the timestamp in the returned
                // OffsetAndTimestamp is always positive.
                if (entry.getValue() < 0)
                    throw new IllegalArgumentException("The target time for partition " + entry.getKey() + " is " +
                            entry.getValue() + ". The target time cannot be negative.");
            }
            return offsetFetcher.offsetsForTimes(timestampsToSearch, time.timer(timeout));
        } finally {
            release();
        }
    }

    @Override
    public Map<TopicPartition, Long> beginningOffsets(Collection<TopicPartition> partitions) {
        return beginningOffsets(partitions, Duration.ofMillis(defaultApiTimeoutMs));
    }

    @Override
    public Map<TopicPartition, Long> beginningOffsets(Collection<TopicPartition> partitions, Duration timeout) {
        acquireAndEnsureOpen();
        try {
            return offsetFetcher.beginningOffsets(partitions, time.timer(timeout));

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Clamp timestamps to >= 0 before calling: ts = Math.max(0L, ts).
  2. Use ListOffsets/offsetsForTimes with 0 to mean 'earliest'; do not use negative sentinels.
  3. Validate time math units are milliseconds and that subtraction cannot go negative.

Example fix

// before
long ts = now - lookbackMs; // may be negative
consumer.offsetsForTimes(Map.of(tp, ts));

// after
long ts = Math.max(0L, now - lookbackMs);
consumer.offsetsForTimes(Map.of(tp, ts));
Defensive patterns

Strategy: validation

Validate before calling

// Clamp/normalize timestamps before offsetsForTimes:
Map<TopicPartition, Long> safe = new HashMap<>();
for (var e : timestampsToSearch.entrySet()) {
    long t = e.getValue();
    if (t < 0) t = 0; // or use ListOffsetsRequest.EARLIEST_TIMESTAMP semantics
    safe.put(e.getKey(), t);
}
return consumer.offsetsForTimes(safe, Duration.ofSeconds(30));

Type guard

// A tiny value-object that rejects negatives at construction time:
record NonNegativeTimestamp(long epochMillis) {
    NonNegativeTimestamp {
        if (epochMillis < 0)
            throw new IllegalArgumentException("timestamp must be >= 0");
    }
}
// Build the map from NonNegativeTimestamp values; the type makes the bad state unrepresentable.

Try / catch

// Recover by clamping the offending entry and retrying once:
try {
    return consumer.offsetsForTimes(timestampsToSearch);
} catch (IllegalArgumentException e) {
    if (!e.getMessage().contains("target time cannot be negative")) throw e;
    Map<TopicPartition, Long> clamped = new HashMap<>();
    timestampsToSearch.forEach((k, v) -> clamped.put(k, Math.max(0, v)));
    return consumer.offsetsForTimes(clamped);
}

Prevention

When it happens

Trigger: Passing a timestamp computed from System.currentTimeMillis() minus a future or uninitialized value; using -1 as a 'from beginning' sentinel (use offsetsBeginningOffsets instead); clock skew producing negative durations; off-by-one in time math.

Common situations: Replay logic that does (now - lookbackMs) with lookbackMs greater than now due to bad input; passing Optional.orElse(-1L); unit confusion (seconds vs millis) producing negative values; legacy code migrated from a system using -1 as a sentinel.

Related errors


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