{"record":{"id":"327c24def46a065c","repo":"apache/pulsar","slug":"java-io-ioexception-wraps-interruptedexception","errorCode":null,"errorMessage":"java.io.IOException (wraps InterruptedException | ExecutionException when getting LedgerEntries)","messagePattern":"java\\.io\\.IOException \\(wraps InterruptedException \\| ExecutionException when getting LedgerEntries\\)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/BlockAwareSegmentInputStreamImpl.java","lineNumber":214,"sourceCode":"                ByteBuf buf = entry.getEntryBuffer().retain();\n                int entryLength = buf.readableBytes();\n                long entryId = entry.getEntryId();\n\n                CompositeByteBuf entryBuf = PulsarByteBufAllocator.DEFAULT.compositeBuffer(2);\n                ByteBuf entryHeaderBuf = PulsarByteBufAllocator.DEFAULT.buffer(ENTRY_HEADER_SIZE, ENTRY_HEADER_SIZE);\n\n                entryHeaderBuf.writeInt(entryLength).writeLong(entryId);\n                entryBuf.addComponents(true, entryHeaderBuf, buf);\n\n                entries.add(entryBuf);\n            }\n            return entries;\n        } catch (InterruptedException | ExecutionException e) {\n            log.error().exception(e).log(\"Exception when getting LedgerEntries\");\n            if (e instanceof InterruptedException) {\n                Thread.currentThread().interrupt();\n            }\n            throw new IOException(e);\n        }\n    }\n\n    @Override\n    public int read(byte[] b, int off, int len) throws IOException {\n        if (b == null) {\n            throw new NullPointerException(\"The given bytes are null\");\n        } else if (off < 0 || len < 0 || len > b.length - off) {\n            throw new IndexOutOfBoundsException(\"off=\" + off + \", len=\" + len + \", b.length=\" + b.length);\n        } else if (len == 0) {\n            return 0;\n        }\n\n        int offset = off;\n        int readLen = len;\n        int readBytes = 0;\n        // reading header\n        if (dataBlockHeaderStream.available() > 0) {","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/tiered-storage/jcloud/src/main/java/org/apache/bookkeeper/mledger/offload/jcloud/impl/BlockAwareSegmentInputStreamImpl.java#L196-L232","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped cause: if the source ledger was deleted after offload, read from the offload copy instead of the original ledger","Check bookie availability and the BK client's read timeout/operation settings","Avoid closing/interrupting the BookKeeper client thread performing offload reads; restore the interrupt flag if InterruptedException","Retry the read once the BookKeeper cluster is healthy"],"exampleFix":"// before\nreadStream.read(buf); // IOException wrapping ExecutionException\n// after\ntry { readStream.read(buf); }\ncatch (IOException e) {\n  if (e.getCause() instanceof ExecutionException\n      && rootCause(e) instanceof LedgerDeletedException) {\n    readFromOffloadCopy();\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// ensure the source ledger still exists before reading entries\ntry {\n  bk.openLedger(ledgerId, digestType, password).close();\n} catch (BKException.BKNoSuchLedgerExistsException e) {\n  useOffloadCopy(ledgerId); // ledger already deleted after offload\n}","typeGuard":null,"tryCatchPattern":"try {\n  int n = stream.read(buf);\n} catch (IOException e) {\n  Throwable root = rootCause(e);\n  if (root instanceof InterruptedException) {\n    Thread.currentThread().interrupt();\n  } else if (root instanceof BKException) {\n    log.error(\"BK read failed for offloaded segment: {}\", String.valueOf(root));\n  }\n}","preventionTips":["Check the offload+delete policy before reading original ledgers","Keep the BookKeeper client alive for the whole offload/read operation","Monitor bookie availability and read-timeout configs","Preserve interrupt flags; never swallow InterruptedException"],"tags":["io","bookkeeper","tiered-storage","interrupt"],"backgroundTag":"ledger-entry-read-failure","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}