apache/pulsar · error · IOException

java.io.IOException (wraps InterruptedException | ExecutionE

Error message

java.io.IOException (wraps InterruptedException | ExecutionException when getting LedgerEntries)

What it means

BlockAwareSegmentInputStreamImpl.readNextEntriesFromLedger fetches ledger entries from BookKeeper asynchronously and blocks on the future. If the future completes with InterruptedException or ExecutionException while getting LedgerEntries, the exception is logged and wrapped in a java.io.IOException so the InputStream contract is preserved.

Source

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

                ByteBuf buf = entry.getEntryBuffer().retain();
                int entryLength = buf.readableBytes();
                long entryId = entry.getEntryId();

                CompositeByteBuf entryBuf = PulsarByteBufAllocator.DEFAULT.compositeBuffer(2);
                ByteBuf entryHeaderBuf = PulsarByteBufAllocator.DEFAULT.buffer(ENTRY_HEADER_SIZE, ENTRY_HEADER_SIZE);

                entryHeaderBuf.writeInt(entryLength).writeLong(entryId);
                entryBuf.addComponents(true, entryHeaderBuf, buf);

                entries.add(entryBuf);
            }
            return entries;
        } catch (InterruptedException | ExecutionException e) {
            log.error().exception(e).log("Exception when getting LedgerEntries");
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            throw new IOException(e);
        }
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException("The given bytes are null");
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException("off=" + off + ", len=" + len + ", b.length=" + b.length);
        } else if (len == 0) {
            return 0;
        }

        int offset = off;
        int readLen = len;
        int readBytes = 0;
        // reading header
        if (dataBlockHeaderStream.available() > 0) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Inspect the wrapped cause: if the source ledger was deleted after offload, read from the offload copy instead of the original ledger
  2. Check bookie availability and the BK client's read timeout/operation settings
  3. Avoid closing/interrupting the BookKeeper client thread performing offload reads; restore the interrupt flag if InterruptedException
  4. Retry the read once the BookKeeper cluster is healthy

Example fix

// before
readStream.read(buf); // IOException wrapping ExecutionException
// after
try { readStream.read(buf); }
catch (IOException e) {
  if (e.getCause() instanceof ExecutionException
      && rootCause(e) instanceof LedgerDeletedException) {
    readFromOffloadCopy();
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the source ledger still exists before reading entries
try {
  bk.openLedger(ledgerId, digestType, password).close();
} catch (BKException.BKNoSuchLedgerExistsException e) {
  useOffloadCopy(ledgerId); // ledger already deleted after offload
}

Try / catch

try {
  int n = stream.read(buf);
} catch (IOException e) {
  Throwable root = rootCause(e);
  if (root instanceof InterruptedException) {
    Thread.currentThread().interrupt();
  } else if (root instanceof BKException) {
    log.error("BK read failed for offloaded segment: {}", String.valueOf(root));
  }
}

Prevention

When it happens

Trigger: readEntries -> readNextEntriesFromLedger calls ledger.asyncReadEntries(...).get(); BK client errors (LedgerDeletedException, NoSuchEntryException, client closed), timeouts, or thread interruption surface as ExecutionException/InterruptedException and become this IOException.

Common situations: Offloaded-segment read while the source ledger was deleted (after offload+delete policy) or ledgers were removed from bookies; BookKeeper client shutting down during offload/read; bookie unavailability causing read timeout.

Related errors


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