apache/pulsar · error · IOException

Error preloading next range

Error message

Error preloading next range

What it means

IOException 'Error preloading next range' thrown by LegacyHierarchicalLedgerRangeIterator.preload when the blocking future that fetches the next ledger range from the metadata store completes with ExecutionException or times out (TimeoutException). It signals a failure or slowness of the underlying metadata store while enumerating ledger hierarchy nodes.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/LegacyHierarchicalLedgerRangeIterator.java:115

    }

    private synchronized void preload() throws IOException {
        while (nextRange == null && !iteratorDone) {
            boolean hasMoreElements = false;
            try {
                if (l1NodesIter == null) {
                    List<String> l1Nodes = store.sync(ledgersRoot)
                            .thenCompose(__ -> store.getChildrenFromStore(ledgersRoot))
                            .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
                    l1NodesIter = l1Nodes.iterator();
                    hasMoreElements = nextL1Node();
                } else if (l2NodesIter == null || !l2NodesIter.hasNext()) {
                    hasMoreElements = nextL1Node();
                } else {
                    hasMoreElements = true;
                }
            } catch (ExecutionException | TimeoutException ke) {
                throw new IOException("Error preloading next range", ke);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new IOException("Interrupted while preloading", ie);
            }
            if (hasMoreElements) {
                nextRange = getLedgerRangeByLevel(curL1Nodes, l2NodesIter.next());
                if (nextRange.size() == 0) {
                    nextRange = null;
                }
            } else {
                iteratorDone = true;
            }
        }
    }

    @Override
    public synchronized boolean hasNext() throws IOException {
        preload();

View on GitHub (pinned to 820761864e)

Solutions

  1. Check metadata-store health, latency, and network connectivity between client and store
  2. Increase the BLOCKING_CALL_TIMEOUT if the hierarchy is large and calls legitimately exceed it
  3. Retry the iteration after the metadata store recovers; inspect the wrapped cause (ke.getCause()) for the root error

Example fix

// before
LedgerRangeIterator it = lm.getLedgerRanges(...); // throws IOException on slow ZK
// after
try {
    LedgerRangeIterator it = lm.getLedgerRanges(...);
} catch (IOException e) {
    if (e.getMessage().startsWith("Error preloading")) {
        LOG.warn("Metadata store slow/unavailable during ledger enumeration", e); // retry later
    }
    throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-check store health/latency before long enumerations
store.getChildrenFromStore(ledgersRoot).orTimeout(5, TimeUnit.SECONDS).join();

Try / catch

try {
    while (it.hasNext()) { process(it.next()); }
} catch (IOException e) {
    if (e.getMessage().startsWith("Error preloading")) {
        // retry after checking metadata-store health; inspect e.getCause()
    }
    throw e;
}

Prevention

When it happens

Trigger: hasNext() triggers preload(); the store.sync(...).thenCompose(getChildrenFromStore...) future fails or exceeds BLOCKING_CALL_TIMEOUT, surfacing as ExecutionException/TimeoutException.

Common situations: Slow or overloaded ZooKeeper/metadata store; network partitions; blocking-call timeout too small for large ledger hierarchies; metadata store session expiry.

Related errors


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