apache/pulsar · error · IndexOutOfBoundsException

Entry index: ${messageEntryId} beyond lastEntryId: ${lastEnt

Error message

Entry index: ${messageEntryId} beyond lastEntryId: ${lastEntryId}

What it means

getIndexEntryForEntry looks up the data-block location of a ledger entry in the offload index. If the requested entry id exceeds the segment metadata's lastEntryId, the entry cannot exist in this offloaded segment, so an IndexOutOfBoundsException is thrown after a warning log. This is a caller-input validation failure, not corruption.

Source

Thrown at tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/OffloadIndexBlockImpl.java:108

    public void recycle() {
        dataObjectLength = -1;
        dataHeaderLength = -1;
        segmentMetadata = null;
        indexEntries.clear();
        indexEntries = null;
        if (recyclerHandle != null) {
            recyclerHandle.recycle(this);
        }
    }

    @Override
    public OffloadIndexEntry getIndexEntryForEntry(long messageEntryId) throws IOException {
        if (messageEntryId > segmentMetadata.getLastEntryId()) {
            log.warn().attr("entryId", messageEntryId)
                    .attr("lastEntryId", segmentMetadata.getLastEntryId())
                    .log("Requested entry beyond lastEntryId");
            throw new IndexOutOfBoundsException("Entry index: " + messageEntryId
                + " beyond lastEntryId: " + segmentMetadata.getLastEntryId());
        }
        // find the greatest mapping Id whose entryId <= messageEntryId
        return this.indexEntries.floorEntry(messageEntryId).getValue();
    }

    @Override
    public int getEntryCount() {
        return this.indexEntries.size();
    }

    @Override
    public LedgerMetadata getLedgerMetadata() {
        return this.segmentMetadata;
    }

    @Override
    public long getDataObjectLength() {

View on GitHub (pinned to 820761864e)

Solutions

  1. Check entryId against segment metadata lastEntryId before calling, and read newer entries from BookKeeper instead
  2. Re-offload the ledger if data written after the previous offload should now be offloaded
  3. Ensure callers iterate only entry ids within the offloaded segment's range
  4. Fix off-by-one logic: valid ids are <= lastEntryId in the segment

Example fix

// before: blind lookup
OffloadIndexEntry e = index.getIndexEntryForEntry(entryId);
// after: bounds-check and fall back to BookKeeper
if (entryId > index.getSegmentMetadata().getLastEntryId()) {
    return readEntryFromBookKeeper(ledgerId, entryId);
}
OffloadIndexEntry e = index.getIndexEntryForEntry(entryId);
Defensive patterns

Strategy: validation

Validate before calling

if (messageEntryId > index.getSegmentMetadata().getLastEntryId()) {
    // entry not offloaded; must be read from BookKeeper instead
    return readFromBookKeeper(ledgerId, messageEntryId);
}

Try / catch

try {
    OffloadIndexEntry e = index.getIndexEntryForEntry(messageEntryId);
} catch (IndexOutOfBoundsException e) {
    // fall back to BookKeeper for entries beyond the offloaded segment
}

Prevention

When it happens

Trigger: Calling getIndexEntryForEntry(messageEntryId) with an entry id greater than segmentMetadata.getLastEntryId() — e.g. requesting entries that were written to the ledger after it was offloaded, or passing a wrong/uninitialized entry id.

Common situations: Reading entries from a ledger that has continued appending after offload without re-reading them from BookKeeper; cursor cache assuming offloaded data covers the whole ledger; off-by-one or wrong-segment logic when iterating entries.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/8655cafa53802ce8. Report an issue: GitHub.