apache/pulsar · error · IOException

IOException

Error message

IOException

What it means

PulsarLayoutManager.readLedgerLayout() reads the LedgerLayout node from the metadata store and blocks on the future. Any metadata store failure (BookieException.MetadataStoreException 'Layout node not found', ExecutionException, TimeoutException) or interruption is wrapped in an IOException because the BookKeeperLayoutManager API is synchronous.Thrown when layout data cannot be retrieved within BLOCKING_CALL_TIMEOUT.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLayoutManager.java:62

    private final String layoutPath;

    public PulsarLayoutManager(MetadataStoreExtended store, String ledgersRootPath) {
        this.ledgersRootPath = ledgersRootPath;
        this.store = store;
        this.layoutPath = ledgersRootPath + "/" + BookKeeperConstants.LAYOUT_ZNODE;
    }

    @Override
    public LedgerLayout readLedgerLayout() throws IOException {
        try {
            byte[] layoutData = store.get(layoutPath).get(BLOCKING_CALL_TIMEOUT, MILLISECONDS)
                    .orElseThrow(() -> new BookieException.MetadataStoreException("Layout node not found"))
                    .getValue();
            return LedgerLayout.parseLayout(layoutData);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException(e);
        } catch (BookieException | ExecutionException | TimeoutException e) {
            throw new IOException(e);
        }
    }

    @Override
    public void storeLedgerLayout(LedgerLayout ledgerLayout) throws IOException {
        try {
            byte[] layoutData = ledgerLayout.serialize();

            store.put(layoutPath, layoutData, Optional.of(-1L))
                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException(e);
        } catch (TimeoutException e) {
            throw new IOException(e);
        } catch (ExecutionException e) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Check that the ledgers root path and layout node exist in the metadata store; write the layout (storeLedgerLayout) if bootstrapping.
  2. Verify metadataStoreUrl configuration matches the cluster's actual store layout.
  3. Check metadata store health and increase timeout if reads are slow.
  4. Inspect IOException#getCause for the concrete BookieException/TimeoutException.

Example fix

// before
LedgerLayout layout = layoutManager.readLedgerLayout(); // fails on fresh cluster
// after
byte[] data = store.get(layoutPath).get();
if (data.isEmpty()) {
    layoutManager.storeLedgerLayout(LedgerLayout.of(...)); // bootstrap layout first
}
LedgerLayout layout = layoutManager.readLedgerLayout();
Defensive patterns

Strategy: try-catch

Validate before calling

// verify layout node exists before reading
boolean exists = store.exists(layoutPath).get(30, TimeUnit.SECONDS);

Try / catch

try { layout = mgr.readLedgerLayout(); }
catch (IOException e) {
  Throwable c = e.getCause();
  if (c instanceof BookieException.MetadataStoreException) { /* bootstrap layout or fix path */ }
  else if (c instanceof TimeoutException) { /* retry */ }
  else throw e;
}

Prevention

When it happens

Trigger: Calling readLedgerLayout() when the layout node (e.g. /ledgers/LAYOUT) does not exist, the metadata store is unreachable, the read times out, or the thread is interrupted.

Common situations: Fresh clusters before any layout was written; misconfigured ledgersRootPath pointing to the wrong znode prefix; metadata store outages during bookie/broker startup; interrupted shutdown.

Related errors


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