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
- Check entryId against segment metadata lastEntryId before calling, and read newer entries from BookKeeper instead
- Re-offload the ledger if data written after the previous offload should now be offloaded
- Ensure callers iterate only entry ids within the offloaded segment's range
- 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
- Track the offloaded entry range per ledger in the reader
- Route entries beyond lastEntryId to BookKeeper reads
- Re-offload after significant post-offload writes if storage reads are needed
- Avoid assuming offloaded data covers the full ledger
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
- Entry index: ${messageEntryId} beyond lastEntryId: ${lastEnt
- The '${offloaderName}' offloader does not provide an offload
- Class ${factoryClass} does not implement interface ${interfa
- Error offloading: ${lastError}
- Invalid namespace format. namespace: ${namespace}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/8655cafa53802ce8.
Report an issue: GitHub.