apache/pulsar · warning · LedgerLayoutExistsException

LedgerLayoutExistsException

Error message

LedgerLayoutExistsException

What it means

LedgerLayoutExistsException is thrown by storeLedgerLayout when the metadata put with expected version -1 fails with MetadataStoreException.BadVersionException, meaning the layout node already exists. This is BookKeeper's conditional-create semantics: the layout may only be written once; the exception is part of the LayoutManager contract.

Source

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

            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) {
            if (e.getCause() instanceof MetadataStoreException.BadVersionException) {
                throw new LedgerLayoutExistsException(e);
            } else {
                throw new IOException(e);
            }
        }
    }

    @Override
    public void deleteLedgerLayout() throws IOException {
        try {
            store.delete(layoutPath, Optional.empty())
                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException(e);
        } catch (ExecutionException | TimeoutException e) {
            throw new IOException(e);
        }
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Treat LedgerLayoutExistsException as benign when re-initializing: call readLedgerLayout() to load and validate the existing layout instead of storing a new one.
  2. Ensure only one component performs initial layout creation (serialize cluster bootstrap).
  3. If the existing layout is wrong or from another cluster, deliberately delete it with deleteLedgerLayout() and re-store — only after confirming the ledgers root is unused/migratable.

Example fix

// before
layoutManager.storeLedgerLayout(layout);

// after
try {
    layoutManager.storeLedgerLayout(layout);
} catch (LedgerLayoutExistsException e) {
    LedgerLayout existing = layoutManager.readLedgerLayout();
    if (!existing.equals(layout)) {
        throw new IOException("Layout mismatch with existing cluster layout", e);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

LedgerLayout existing = layoutManager.readLedgerLayout();
if (existing != null && !existing.equals(newLayout)) {
    throw new IllegalStateException("Ledger layout already exists and differs");
}

Type guard

boolean layoutMatchesExisting(PulsarLayoutManager lm, LedgerLayout desired) {
    try { return desired.equals(lm.readLedgerLayout()); }
    catch (IOException missing) { return false; } // no layout yet
}

Try / catch

try {
    layoutManager.storeLedgerLayout(layout);
} catch (LedgerLayoutExistsException e) {
    // idempotent bootstrap: verify and continue
    if (!layout.equals(layoutManager.readLedgerLayout())) throw new IOException("layout mismatch", e);
}

Prevention

When it happens

Trigger: Two concurrent BookKeeper clients (or two broker starts) both attempt storeLedgerLayout with expect -1; one wins and the other gets BadVersionException converted to LedgerLayoutExistsException. Also occurs on re-initialization against a metadata store that already holds a layout.

Common situations: Starting a second broker against an already-initialized metadata store; race between multiple BookKeeper client instances; replaying an initialization script; connecting a new cluster to a ledgers root that already has a layout (e.g., after switching ledgersRootPath or reusing a ZooKeeper chroot).

Related errors


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