{"record":{"id":"1fb0e0d6588d6841","repo":"apache/pulsar","slug":"ledgerlayoutexistsexception","errorCode":null,"errorMessage":"LedgerLayoutExistsException","messagePattern":"LedgerLayoutExistsException","errorType":"exception","errorClass":"LedgerLayoutExistsException","httpStatus":null,"severity":"warning","filePath":"pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLayoutManager.java","lineNumber":82,"sourceCode":"            throw new IOException(e);\n        }\n    }\n\n    @Override\n    public void storeLedgerLayout(LedgerLayout ledgerLayout) throws IOException {\n        try {\n            byte[] layoutData = ledgerLayout.serialize();\n\n            store.put(layoutPath, layoutData, Optional.of(-1L))\n                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new IOException(e);\n        } catch (TimeoutException e) {\n            throw new IOException(e);\n        } catch (ExecutionException e) {\n            if (e.getCause() instanceof MetadataStoreException.BadVersionException) {\n                throw new LedgerLayoutExistsException(e);\n            } else {\n                throw new IOException(e);\n            }\n        }\n    }\n\n    @Override\n    public void deleteLedgerLayout() throws IOException {\n        try {\n            store.delete(layoutPath, Optional.empty())\n                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new IOException(e);\n        } catch (ExecutionException | TimeoutException e) {\n            throw new IOException(e);\n        }\n    }","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLayoutManager.java#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Treat LedgerLayoutExistsException as benign when re-initializing: call readLedgerLayout() to load and validate the existing layout instead of storing a new one.","Ensure only one component performs initial layout creation (serialize cluster bootstrap).","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."],"exampleFix":"// before\nlayoutManager.storeLedgerLayout(layout);\n\n// after\ntry {\n    layoutManager.storeLedgerLayout(layout);\n} catch (LedgerLayoutExistsException e) {\n    LedgerLayout existing = layoutManager.readLedgerLayout();\n    if (!existing.equals(layout)) {\n        throw new IOException(\"Layout mismatch with existing cluster layout\", e);\n    }\n}","handlingStrategy":"validation","validationCode":"LedgerLayout existing = layoutManager.readLedgerLayout();\nif (existing != null && !existing.equals(newLayout)) {\n    throw new IllegalStateException(\"Ledger layout already exists and differs\");\n}","typeGuard":"boolean layoutMatchesExisting(PulsarLayoutManager lm, LedgerLayout desired) {\n    try { return desired.equals(lm.readLedgerLayout()); }\n    catch (IOException missing) { return false; } // no layout yet\n}","tryCatchPattern":"try {\n    layoutManager.storeLedgerLayout(layout);\n} catch (LedgerLayoutExistsException e) {\n    // idempotent bootstrap: verify and continue\n    if (!layout.equals(layoutManager.readLedgerLayout())) throw new IOException(\"layout mismatch\", e);\n}","preventionTips":["Serialize cluster bootstrap so only one node creates the layout.","Check readLedgerLayout() before writing when reusing an existing ledgers root.","Never point a new cluster at a ledgersRootPath that already contains a layout without verifying it."],"tags":["bookkeeper","layout","concurrent-write","bad-version"],"backgroundTag":"ledger-layout-already-exists","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"}