apache/pulsar · error · IOException
Error seeking, new position %d < current position %d
Error message
Error seeking, new position %d < current position %d
What it means
BlobStoreBackedInputStreamImpl is a forward-only stream over offloaded ledger data; seekForward(position) must never move the cursor backwards. If a caller requests a position earlier than the current cursor, it throws an IOException since the buffer cannot rewind what has already been consumed.
Source
Thrown at tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/BlobStoreBackedInputStreamImpl.java:188
log.debug().attr("position", position).attr("bucket", bucket).attr("key", key)
.attr("cursor", cursor).attr("bufStart", bufferOffsetStart)
.attr("bufEnd", bufferOffsetEnd).log("Seeking");
if (position >= bufferOffsetStart && position <= bufferOffsetEnd) {
long newIndex = position - bufferOffsetStart;
buffer.readerIndex((int) newIndex);
} else {
bufferOffsetStart = bufferOffsetEnd = -1;
this.cursor = position;
buffer.clear();
}
}
@Override
public void seekForward(long position) throws IOException {
if (position >= cursor) {
seek(position);
} else {
throw new IOException(String.format("Error seeking, new position %d < current position %d",
position, cursor));
}
}
public long getCurrentPosition() {
if (bufferOffsetStart != -1) {
return bufferOffsetStart + buffer.readerIndex();
}
return cursor + buffer.readerIndex();
}
@Override
public void close() {
buffer.release();
}
@Override
public int available() throws IOException {View on GitHub (pinned to 820761864e)
Solutions
- Ensure callers only request monotonically increasing positions when reading offloaded ledgers; re-open a fresh ReadHandle if backward access is needed
- Fix entry-position computation so reads are issued in ascending order (verify offload thresholds and entry offsets)
- If a rewind is genuinely required, recreate the input stream/ReadHandle instead of seeking backward
Example fix
// before: rewind attempt
readHandle.seekForward(oldPosition); // throws
// after: reopen for backward reads
if (oldPosition < stream.getCurrentPosition()) {
stream = reopenReadHandle(ledgerId);
}
stream.seekForward(oldPosition); Defensive patterns
Strategy: validation
Validate before calling
// guard before seeking
long cur = stream.getCurrentPosition();
if (position < cur) {
throw new IllegalArgumentException("cannot seek backward: " + position + " < " + cur);
} Try / catch
try {
stream.seekForward(position);
} catch (IOException e) {
if (e.getMessage().contains("new position") && e.getMessage().contains("< current position")) {
stream = reopenReadHandle(ledgerId); // rewind requires a fresh stream
} else throw e;
} Prevention
- Read offloaded ledgers in strictly ascending entry order
- Track the stream's current position before every seek
- Never cache and replay older positions on the same stream instance
- Reopen the ReadHandle when backward reads are required
When it happens
Trigger: BookKeeper read-handle seek logic calls seekForward with a position smaller than the stream's current position — typically when ledger entry reads are issued out of order or a caller re-reads an entry already passed.
Common situations: Reading entries of an offloaded ledger non-monotonically (e.g. after a consumer/reader rewind or a wrong entry-id computation); caller caching stale entry positions and re-reading; bug in ReadHandle interpolation of offload positions.
Related errors
- Fail to read LedgerMetadata for ledgerId ${key}
- java.io.IOException (wraps InterruptedException | ExecutionE
- Data block header magic word not match. read: ${magic} expec
- Cursor %s mark-delete position %s is ahead of the last posit
- IOException
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/ca58568644c68cac.
Report an issue: GitHub.