apache/pulsar · warning · IOException
Interrupted while reading ledgers at path ${path}
Error message
Interrupted while reading ledgers at path ${path} What it means
getChildrenAt() blocks on a CompletableFuture with .get(timeout); if the waiting thread is interrupted, it re-asserts the interrupt flag and wraps the InterruptedException in an IOException with the message 'Interrupted while reading ledgers at path <path>'. This is not a store failure — the caller's thread was interrupted while blocked.
Source
Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/LongHierarchicalLedgerRangeIterator.java:69
* Returns all children with path as a parent. If path is non-existent,
* returns an empty list anyway (after all, there are no children there).
* Maps all exceptions (other than NoNode) to IOException in keeping with
* LedgerRangeIterator.
*
* @param path
* @return Iterator into set of all children with path as a parent
* @throws IOException
*/
List<String> getChildrenAt(String path) throws IOException {
try {
return store.sync(path).thenCompose(__ -> store.getChildrenFromStore(path))
.get(AbstractMetadataDriver.BLOCKING_CALL_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (ExecutionException | TimeoutException e) {
log.debug().attr("path", path).log("Failed to get children");
throw new IOException(e);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted while reading ledgers at path " + path, ie);
}
}
/**
* Represents the ledger range rooted at a leaf node, returns at most one LedgerRange.
*/
class LeafIterator implements LedgerManager.LedgerRangeIterator {
// Null iff iteration is complete
LedgerManager.LedgerRange range;
LeafIterator(String path) throws IOException {
List<String> ledgerLeafNodes = getChildrenAt(path);
Set<Long> ledgerIds = HierarchicalLedgerUtils.ledgerListToSet(ledgerLeafNodes, ledgerRootPath, path);
log.debug().attr("hashNode", path)
.attr("ledgers", ledgerIds)
.log("All active ledgers from ZK for hash node");
if (!ledgerIds.isEmpty()) {
range = new LedgerManager.LedgerRange(ledgerIds);View on GitHub (pinned to 820761864e)
Solutions
- Let shutdown proceed — this is expected during termination; stop the iteration.
- Avoid interrupting worker threads running ledger scans, or run scans in a cancellable task you control.
- If seen unexpectedly, audit for code calling interrupt() on threads doing metadata reads.
- Preserve and honor the interrupt flag (the library already does Thread.currentThread().interrupt()); do not swallow it in retry loops.
Example fix
// before
catch (IOException e) { e.printStackTrace(); /* keep iterating */ }
// after
catch (IOException e) {
if (Thread.currentThread().isInterrupted()) {
return; // shutdown in progress, stop scanning
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// check interrupt status before starting the scan
if (Thread.currentThread().isInterrupted()) { return; } Try / catch
try { ... } catch (IOException e) {
if (Thread.currentThread().isInterrupted()) { /* stop cleanly */ return; }
throw e;
} Prevention
- Don't call shutdownNow() on threads doing metadata reads unless aborting
- Check Thread.interrupted() in long loops
- Make shutdown paths stop scans gracefully
- Keep scans short-lived or cancellable
When it happens
Trigger: Thread interruption while getChildrenAt(path) is blocked on .get(..., BLOCKING_CALL_TIMEOUT, ...) — typically broker shutdown, task cancellation, or an executor that interrupts workers.
Common situations: Graceful shutdown of a broker/bookie mid ledger-range scan; a cancelled scheduled job iterating ledgers; thread pool shutdownNow during long hierarchical ledger enumeration.
Related errors
- IOException
- Interrupted while contacting metadata store
- Timeout during managed ledger close
- Interrupted initializing OAuth2 IdP TLS factory
- Interrupted at fetching schema info for <SchemaUtils.getStri
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/d4f99d192a1237ad.
Report an issue: GitHub.