apache/pulsar · error · IndexOutOfBoundsException
Entry index: ${messageEntryId} beyond lastEntryId: ${lastEnt
Error message
Entry index: ${messageEntryId} beyond lastEntryId: ${lastEntryId} What it means
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.
Source
Thrown at tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/OffloadIndexBlockV2Impl.java:120
public void recycle() {
dataObjectLength = -1;
dataHeaderLength = -1;
segmentMetadata = null;
indexEntries.clear();
indexEntries = null;
if (recyclerHandle != null) {
recyclerHandle.recycle(this);
}
}
@Override
public OffloadIndexEntry getIndexEntryForEntry(long ledgerId, long messageEntryId) throws IOException {
if (messageEntryId > getLedgerMetadata(ledgerId).getLastEntryId()) {
log.warn().attr("entryId", messageEntryId)
.attr("lastEntryId", getLedgerMetadata(ledgerId).getLastEntryId())
.log("Requested entry beyond lastEntryId");
throw new IndexOutOfBoundsException("Entry index: " + messageEntryId
+ " beyond lastEntryId: " + getLedgerMetadata(ledgerId).getLastEntryId());
}
// find the greatest mapping Id whose entryId <= messageEntryId
return this.indexEntries.get(ledgerId).floorEntry(messageEntryId).getValue();
}
public long getStartEntryId(long ledgerId) {
return this.indexEntries.get(ledgerId).firstEntry().getValue().getEntryId();
}
@Override
public int getEntryCount() {
int ans = 0;
for (TreeMap<Long, OffloadIndexEntryImpl> v : this.indexEntries.values()) {
ans += v.size();
}
return ans;View on GitHub (pinned to 820761864e)
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
Example fix
// before: direct lookup
OffloadIndexEntry e = index.getIndexEntryForEntry(ledgerId, entryId);
// after: validate range first
LedgerInfo meta = index.getLedgerMetadata(ledgerId);
if (entryId > meta.getLastEntryId()) {
return readEntryFromBookKeeper(ledgerId, entryId);
}
OffloadIndexEntry e = index.getIndexEntryForEntry(ledgerId, entryId); Defensive patterns
Strategy: validation
Validate before calling
LedgerInfo meta = index.getLedgerMetadata(ledgerId);
if (messageEntryId > meta.getLastEntryId()) {
return readFromBookKeeper(ledgerId, messageEntryId); // not offloaded
} Try / catch
try {
OffloadIndexEntry e = index.getIndexEntryForEntry(ledgerId, messageEntryId);
} catch (IndexOutOfBoundsException e) {
// fall back to BookKeeper for entries past the offloaded segment
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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/25fb2bc2af2e63bc.
Report an issue: GitHub.