apache/kafka · error · java.lang.IllegalArgumentException
seek offset must not be a negative number
Error message
seek offset must not be a negative number
What it means
IllegalArgumentException thrown at the entry of AsyncKafkaConsumer.seek(TopicPartition, long) when the supplied offset is < 0. Offsets are non-negative monotonically increasing positions within a partition; a negative offset has no valid meaning and is rejected before any network I/O. This guard runs before acquireAndEnsureOpen(), so it fires regardless of consumer state.
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/AsyncKafkaConsumer.java:1156
throwIfGroupIdNotDefined();
offsetCommitCallbackInvoker.executeCallbacks();
if (commitEvent.offsets().isPresent() && commitEvent.offsets().get().isEmpty()) {
return CompletableFuture.completedFuture(null);
}
applicationEventHandler.add(commitEvent);
// This blocks until the background thread retrieves allConsumed positions to commit if none were explicitly specified.
// This operation will ensure that the offsets to commit are not affected by fetches which may start after this
ConsumerUtils.getResult(commitEvent.offsetsReady(), defaultApiTimeoutMs.toMillis());
return commitEvent.future();
}
@Override
public void seek(TopicPartition partition, long offset) {
if (offset < 0)
throw new IllegalArgumentException("seek offset must not be a negative number");
acquireAndEnsureOpen();
try {
log.info("Seeking to offset {} for partition {}", offset, partition);
SeekUnvalidatedEvent seekUnvalidatedEventEvent = new SeekUnvalidatedEvent(
defaultApiTimeoutDeadlineMs(),
partition,
offset,
Optional.empty()
);
applicationEventHandler.addAndGet(seekUnvalidatedEventEvent);
} finally {
release();
}
}
@Override
public void seek(TopicPartition partition, OffsetAndMetadata offsetAndMetadata) {View on GitHub (pinned to c31c9215e1)
Solutions
- Validate the offset is >= 0 before calling seek; if it is -1 or unknown, call seekToBeginning/seekToEnd or skip the seek.
- Fix the upstream offset computation: clamp at 0, or branch when the source offset is uninitialized.
- Replace sentinel-style usage (-1, -2) with the dedicated seekToBeginning(Collection) / seekToEnd(Collection) APIs.
Example fix
// before
long offset = committedOffset != null ? committedOffset - lookback : -1;
consumer.seek(tp, offset);
// after
if (committedOffset == null) {
consumer.seekToBeginning(List.of(tp));
} else {
consumer.seek(tp, Math.max(0, committedOffset - lookback));
} Defensive patterns
Strategy: type-guard
Validate before calling
// seek(TopicPartition, long) — guard the offset at the call site.
static void safeSeek(Consumer<?, ?> c, TopicPartition tp, long offset) {
if (offset < 0)
throw new IllegalArgumentException("seek offset for " + tp + " is negative: " + offset);
c.seek(tp, offset);
}
// If the offset originates from external state (DB, file), coerce defensively:
long parsed = Long.parseLong(stored);
if (parsed < 0) throw new IllegalStateException("Stored offset corrupt (negative): " + stored);
safeSeek(consumer, tp, parsed); Type guard
// Use a non-negative wrapper type so the compiler rejects bad values at the source.
// In Java, a small value class with a static factory that validates:
public final class NonNegativeOffset {
private final long value;
private NonNegativeOffset(long v) { this.value = v; }
public static NonNegativeOffset of(long v) {
if (v < 0) throw new IllegalArgumentException("offset must be >= 0, got " + v);
return new NonNegativeOffset(v);
}
public long value() { return value; }
}
// TypeScript analogue:
// type NonNegativeLong = number & { __brand: 'NonNegative' };
// function nonNegative(n: number): NonNegativeLong {
// if (!Number.isInteger(n) || n < 0) throw new RangeError('negative offset');
// return n as NonNegativeLong;
// }
// function seek(c: Consumer, tp: TopicPartition, off: NonNegativeLong): void { c.seek(tp, off); } Try / catch
// IllegalArgumentException from seek is a programmer error; catch only to enrich
// diagnostics, then propagate (do not retry the same value).
try {
consumer.seek(tp, offset);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(
"Refusing to seek " + tp + " to offset " + offset + "; check the source of this offset", e);
} Prevention
- Treat offsets as unsigned-like quantities: validate >= 0 at every external boundary (DB read, file parse, queue message).
- Persist offsets alongside a checksum or sentinel so a corrupted store is detectable before it reaches seek().
- Default to seekToBeginning/seekToEnd when the stored offset is unusable, rather than passing a sentinel like -1.
- Unit-test seek paths with offset 0 (boundary) and a large positive value to confirm your validation matches the library's.
When it happens
Trigger: Calling consumer.seek(partition, -1) or passing a computed offset that became negative (e.g. committedOffset - N with N > committedOffset, or defaulting an unknown/uninitialized offset to -1). Also triggered by seek to a sentinel like -2 / -1 that older code used to mean 'beginning'/'end' (use seekToBeginning/seekToEnd instead).
Common situations: Offset arithmetic bugs (committedOffset - lookback where lookback exceeds committed); using -1 as a 'not yet set' placeholder that leaks into seek; porting code from an API that accepted negative sentinels; caching layer returning -1 on miss and feeding it to seek; off-by-one in tests.
Related errors
- seek offset must not be a negative number
- Invalid negative offset
- Invalid negative offset
- Partitions collection cannot be null
- Partitions collection cannot be null
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/44f0bd9eb5329cfa.json.
Report an issue: GitHub.