{"id":"3a653570bb73d9cc","repo":"apache/kafka","slug":"seek-offset-must-not-be-a-negative-number-3a6535","errorCode":null,"errorMessage":"seek offset must not be a negative number","messagePattern":"seek offset must not be a negative number","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java","lineNumber":797,"sourceCode":"    }\n\n    @Override\n    public void commitAsync(final Map<TopicPartition, OffsetAndMetadata> offsets, OffsetCommitCallback callback) {\n        acquireAndEnsureOpen();\n        try {\n            throwIfGroupIdNotDefined();\n            log.debug(\"Committing offsets: {}\", offsets);\n            offsets.forEach(this::updateLastSeenEpochIfNewer);\n            coordinator.commitOffsetsAsync(new HashMap<>(offsets), callback);\n        } finally {\n            release();\n        }\n    }\n\n    @Override\n    public void seek(TopicPartition partition, long offset) {\n        if (offset < 0)\n            throw new IllegalArgumentException(\"seek offset must not be a negative number\");\n\n        acquireAndEnsureOpen();\n        try {\n            log.info(\"Seeking to offset {} for partition {}\", offset, partition);\n            SubscriptionState.FetchPosition newPosition = new SubscriptionState.FetchPosition(\n                    offset,\n                    Optional.empty(), // This will ensure we skip validation\n                    this.metadata.currentLeader(partition));\n            this.subscriptions.seekUnvalidated(partition, newPosition);\n        } finally {\n            release();\n        }\n    }\n\n    @Override\n    public void seek(TopicPartition partition, OffsetAndMetadata offsetAndMetadata) {\n        long offset = offsetAndMetadata.offset();\n        if (offset < 0) {","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java#L779-L815","documentation":"Thrown by seek(TopicPartition, long offset) when offset < 0. Kafka offsets are non-negative log positions; a negative offset is meaningless and would corrupt subscription fetch positions, so it is rejected before any subscription state mutation. The guard sits outside the acquireAndEnsureOpen lock so it fails fast.","triggerScenarios":"Calling consumer.seek(partition, -1) or passing a computed offset that underflows/was not initialized. Reading an offset from a store that returns -1 as a sentinel and forwarding it directly.","commonSituations":"Offset store returning -1 (or another negative sentinel) when no committed offset exists. Arithmetic underflow. Misconfigured external offset store (file, DB, Redis) with a default of -1.","solutions":["Guard the offset before calling seek: if (offset < 0) use consumer.seekToBeginning / seekToEnd or position() instead.","Fix the external offset store to return Optional.empty (or a clearly invalid sentinel handled explicitly) rather than -1.","Initialize offsets to 0 (or use auto.offset.reset) so seek never receives a negative value."],"exampleFix":"// before\nlong off = offsetStore.read(topic, partition); // returns -1 when missing\nconsumer.seek(new TopicPartition(topic, partition), off);\n\n// after\nlong off = offsetStore.read(topic, partition);\nTopicPartition tp = new TopicPartition(topic, partition);\nif (off < 0) {\n    consumer.seekToBeginning(Collections.singletonList(tp));\n} else {\n    consumer.seek(tp, off);\n}","handlingStrategy":"validation","validationCode":"// long offset = ...\nif (offset < 0) {\n    throw new IllegalArgumentException(\"seek offset must not be a negative number\");\n}\nconsumer.seek(partition, offset);","typeGuard":"static long requireNonNegativeOffset(long offset) {\n    if (offset < 0) throw new IllegalArgumentException(\"seek offset must not be a negative number\");\n    return offset;\n}","tryCatchPattern":"try {\n    consumer.seek(partition, offset);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"negative number\")) {\n        consumer.seek(partition, 0L); // clamp to earliest\n    } else throw e;\n}","preventionTips":["Offset sources (DB, offset store) must be non-negative; validate before seek","Use consumer.position(partition) as the source of truth rather than external guesses","Guard seek against Long.MIN_MAX sentinels from deserialization"],"tags":["consumer","seek","validation","offset"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}