{"record":{"id":"ca58568644c68cac","repo":"apache/pulsar","slug":"error-seeking-new-position-d-current-position","errorCode":null,"errorMessage":"Error seeking, new position %d < current position %d","messagePattern":"Error seeking, new position (.+?) < current position (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/BlobStoreBackedInputStreamImpl.java","lineNumber":188,"sourceCode":"        log.debug().attr(\"position\", position).attr(\"bucket\", bucket).attr(\"key\", key)\n                .attr(\"cursor\", cursor).attr(\"bufStart\", bufferOffsetStart)\n                .attr(\"bufEnd\", bufferOffsetEnd).log(\"Seeking\");\n        if (position >= bufferOffsetStart && position <= bufferOffsetEnd) {\n            long newIndex = position - bufferOffsetStart;\n            buffer.readerIndex((int) newIndex);\n        } else {\n            bufferOffsetStart = bufferOffsetEnd = -1;\n            this.cursor = position;\n            buffer.clear();\n        }\n    }\n\n    @Override\n    public void seekForward(long position) throws IOException {\n        if (position >= cursor) {\n            seek(position);\n        } else {\n            throw new IOException(String.format(\"Error seeking, new position %d < current position %d\",\n                                                position, cursor));\n        }\n    }\n\n    public long getCurrentPosition() {\n        if (bufferOffsetStart != -1) {\n            return bufferOffsetStart + buffer.readerIndex();\n        }\n        return cursor + buffer.readerIndex();\n    }\n\n    @Override\n    public void close() {\n        buffer.release();\n    }\n\n    @Override\n    public int available() throws IOException {","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/BlobStoreBackedInputStreamImpl.java#L170-L206","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: rewind attempt\nreadHandle.seekForward(oldPosition); // throws\n// after: reopen for backward reads\nif (oldPosition < stream.getCurrentPosition()) {\n  stream = reopenReadHandle(ledgerId);\n}\nstream.seekForward(oldPosition);","handlingStrategy":"validation","validationCode":"// guard before seeking\nlong cur = stream.getCurrentPosition();\nif (position < cur) {\n  throw new IllegalArgumentException(\"cannot seek backward: \" + position + \" < \" + cur);\n}","typeGuard":null,"tryCatchPattern":"try {\n  stream.seekForward(position);\n} catch (IOException e) {\n  if (e.getMessage().contains(\"new position\") && e.getMessage().contains(\"< current position\")) {\n    stream = reopenReadHandle(ledgerId); // rewind requires a fresh stream\n  } else throw e;\n}","preventionTips":["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"],"tags":["io","tiered-storage","bookkeeper","seek"],"backgroundTag":"invalid-seek-position","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}