{"id":"f19d05254a494a52","repo":"apache/kafka","slug":"the-target-time-for-partition-is-the-target-f19d05","errorCode":null,"errorMessage":"The target time for partition {} is {}. The target time cannot be negative.","messagePattern":"The target time for partition (.+?) is (.+?)\\. The target time cannot be negative\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java","lineNumber":1024,"sourceCode":"        } finally {\n            release();\n        }\n    }\n\n    @Override\n    public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch) {\n        return offsetsForTimes(timestampsToSearch, Duration.ofMillis(defaultApiTimeoutMs));\n    }\n\n    @Override\n    public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch, Duration timeout) {\n        acquireAndEnsureOpen();\n        try {\n            for (Map.Entry<TopicPartition, Long> entry : timestampsToSearch.entrySet()) {\n                // we explicitly exclude the earliest and latest offset here so the timestamp in the returned\n                // OffsetAndTimestamp is always positive.\n                if (entry.getValue() < 0)\n                    throw new IllegalArgumentException(\"The target time for partition \" + entry.getKey() + \" is \" +\n                            entry.getValue() + \". The target time cannot be negative.\");\n            }\n            return offsetFetcher.offsetsForTimes(timestampsToSearch, time.timer(timeout));\n        } finally {\n            release();\n        }\n    }\n\n    @Override\n    public Map<TopicPartition, Long> beginningOffsets(Collection<TopicPartition> partitions) {\n        return beginningOffsets(partitions, Duration.ofMillis(defaultApiTimeoutMs));\n    }\n\n    @Override\n    public Map<TopicPartition, Long> beginningOffsets(Collection<TopicPartition> partitions, Duration timeout) {\n        acquireAndEnsureOpen();\n        try {\n            return offsetFetcher.beginningOffsets(partitions, time.timer(timeout));","sourceCodeStart":1006,"sourceCodeEnd":1042,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java#L1006-L1042","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Clamp timestamps to >= 0 before calling: ts = Math.max(0L, ts).","Use ListOffsets/offsetsForTimes with 0 to mean 'earliest'; do not use negative sentinels.","Validate time math units are milliseconds and that subtraction cannot go negative."],"exampleFix":"// before\nlong ts = now - lookbackMs; // may be negative\nconsumer.offsetsForTimes(Map.of(tp, ts));\n\n// after\nlong ts = Math.max(0L, now - lookbackMs);\nconsumer.offsetsForTimes(Map.of(tp, ts));","handlingStrategy":"validation","validationCode":"// Clamp/normalize timestamps before offsetsForTimes:\nMap<TopicPartition, Long> safe = new HashMap<>();\nfor (var e : timestampsToSearch.entrySet()) {\n    long t = e.getValue();\n    if (t < 0) t = 0; // or use ListOffsetsRequest.EARLIEST_TIMESTAMP semantics\n    safe.put(e.getKey(), t);\n}\nreturn consumer.offsetsForTimes(safe, Duration.ofSeconds(30));","typeGuard":"// A tiny value-object that rejects negatives at construction time:\nrecord NonNegativeTimestamp(long epochMillis) {\n    NonNegativeTimestamp {\n        if (epochMillis < 0)\n            throw new IllegalArgumentException(\"timestamp must be >= 0\");\n    }\n}\n// Build the map from NonNegativeTimestamp values; the type makes the bad state unrepresentable.","tryCatchPattern":"// Recover by clamping the offending entry and retrying once:\ntry {\n    return consumer.offsetsForTimes(timestampsToSearch);\n} catch (IllegalArgumentException e) {\n    if (!e.getMessage().contains(\"target time cannot be negative\")) throw e;\n    Map<TopicPartition, Long> clamped = new HashMap<>();\n    timestampsToSearch.forEach((k, v) -> clamped.put(k, Math.max(0, v)));\n    return consumer.offsetsForTimes(clamped);\n}","preventionTips":["Never derive search timestamps from System.currentTimeMillis() differences without Math.max(0, ...) — clock skew can produce negatives.","Reject negative timestamps at the boundary of your own code (CLI parsing, REST handler) so they never propagate to the consumer.","Use the documented sentinels (OffsetResultStrategy.END_OF_PARTITION / earliest/latest) instead of hand-rolled magic numbers."],"tags":["consumer","api-misuse","validation","java"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}