{"record":{"id":"25fb2bc2af2e63bc","repo":"apache/pulsar","slug":"entry-index-messageentryid-beyond-lastentryid-25fb2b","errorCode":null,"errorMessage":"Entry index: ${messageEntryId} beyond lastEntryId: ${lastEntryId}","messagePattern":"Entry index: (.+?) beyond lastEntryId: (.+?)","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/OffloadIndexBlockV2Impl.java","lineNumber":120,"sourceCode":"\n    public void recycle() {\n        dataObjectLength = -1;\n        dataHeaderLength = -1;\n        segmentMetadata = null;\n        indexEntries.clear();\n        indexEntries = null;\n        if (recyclerHandle != null) {\n            recyclerHandle.recycle(this);\n        }\n    }\n\n    @Override\n    public OffloadIndexEntry getIndexEntryForEntry(long ledgerId, long messageEntryId) throws IOException {\n        if (messageEntryId > getLedgerMetadata(ledgerId).getLastEntryId()) {\n            log.warn().attr(\"entryId\", messageEntryId)\n                    .attr(\"lastEntryId\", getLedgerMetadata(ledgerId).getLastEntryId())\n                    .log(\"Requested entry beyond lastEntryId\");\n            throw new IndexOutOfBoundsException(\"Entry index: \" + messageEntryId\n                    + \" beyond lastEntryId: \" + getLedgerMetadata(ledgerId).getLastEntryId());\n        }\n        // find the greatest mapping Id whose entryId <= messageEntryId\n        return this.indexEntries.get(ledgerId).floorEntry(messageEntryId).getValue();\n    }\n\n    public long getStartEntryId(long ledgerId) {\n        return this.indexEntries.get(ledgerId).firstEntry().getValue().getEntryId();\n    }\n\n    @Override\n    public int getEntryCount() {\n        int ans = 0;\n        for (TreeMap<Long, OffloadIndexEntryImpl> v : this.indexEntries.values()) {\n            ans += v.size();\n        }\n\n        return ans;","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/OffloadIndexBlockV2Impl.java#L102-L138","documentation":"The v2 variant of getIndexEntryForEntry validates the requested entry id against the lastEntryId stored in the per-ledger segment metadata for the given ledgerId. Requesting an entry beyond that bound throws IndexOutOfBoundsException because the entry is not contained in the offloaded data for that ledger segment.","triggerScenarios":"Calling getIndexEntryForEntry(ledgerId, messageEntryId) where messageEntryId exceeds getLedgerMetadata(ledgerId).getLastEntryId() — entries written after offload, an unknown ledgerId (which itself would fail earlier), or a wrong entry id from the caller.","commonSituations":"Multi-ledger (v2) indexes where callers mix up which segment covers which entry range; reads for entries still only in BookKeeper; retry logic replaying ids past the segment end.","solutions":["Bounds-check entryId against the ledger's segment metadata before the call and route newer entries to BookKeeper","Re-offload or wait for the next offload cycle if newer data must be served from storage","Ensure per-ledger entry-range bookkeeping in the caller matches the v2 index's per-ledger metadata","Log and inspect getLedgerMetadata(ledgerId).getLastEntryId() when the range expectation differs"],"exampleFix":"// before: direct lookup\nOffloadIndexEntry e = index.getIndexEntryForEntry(ledgerId, entryId);\n// after: validate range first\nLedgerInfo meta = index.getLedgerMetadata(ledgerId);\nif (entryId > meta.getLastEntryId()) {\n    return readEntryFromBookKeeper(ledgerId, entryId);\n}\nOffloadIndexEntry e = index.getIndexEntryForEntry(ledgerId, entryId);","handlingStrategy":"validation","validationCode":"LedgerInfo meta = index.getLedgerMetadata(ledgerId);\nif (messageEntryId > meta.getLastEntryId()) {\n    return readFromBookKeeper(ledgerId, messageEntryId); // not offloaded\n}","typeGuard":null,"tryCatchPattern":"try {\n    OffloadIndexEntry e = index.getIndexEntryForEntry(ledgerId, messageEntryId);\n} catch (IndexOutOfBoundsException e) {\n    // fall back to BookKeeper for entries past the offloaded segment\n}","preventionTips":["Maintain per-ledger offloaded-range metadata in the consumer","Use the correct ledgerId for multi-ledger v2 indexes","Route post-offload writes to BookKeeper reads","Re-offload or compact segments when serving reads from storage is required"],"tags":["index-out-of-bounds","input-validation","tiered-storage"],"backgroundTag":"entry-index-out-of-range","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}