{"record":{"id":"50eebcb3ec062fa2","repo":"apache/pulsar","slug":"managedledger-name-has-already-been-closed","errorCode":null,"errorMessage":"ManagedLedger ${name} has already been closed","messagePattern":"ManagedLedger (.+?) has already been closed","errorType":"exception","errorClass":"ManagedLedgerException","httpStatus":null,"severity":"error","filePath":"managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java","lineNumber":4526,"sourceCode":"\n        return mlInfo;\n    }\n\n    /**\n     * Throws an exception if the managed ledger has been previously fenced.\n     *\n     * @throws ManagedLedgerException\n     */\n    private void checkFenced() throws ManagedLedgerException {\n        if (STATE_UPDATER.get(this).isFenced()) {\n            log.error(\"Attempted to use a fenced managed ledger\");\n            throw new ManagedLedgerFencedException();\n        }\n    }\n\n    private void checkManagedLedgerIsOpen() throws ManagedLedgerException {\n        if (STATE_UPDATER.get(this) == State.Closed) {\n            throw new ManagedLedgerException(\"ManagedLedger \" + name + \" has already been closed\");\n        }\n    }\n\n    @VisibleForTesting\n    public synchronized void setFenced() {\n        log.info().log(\"Moving to Fenced state\");\n        State prev = STATE_UPDATER.getAndSet(this, State.Fenced);\n        if (prev != State.Fenced) {\n            clearPendingAddEntries(new ManagedLedgerFencedException(\"ManagedLedger \"\n                + name + \" is fenced\"));\n        }\n    }\n\n    synchronized void setFencedForDeletion() {\n        log.info().log(\"Moving to FencedForDeletion state\");\n        State prev = STATE_UPDATER.getAndSet(this, State.FencedForDeletion);\n        if (prev != State.FencedForDeletion) {\n            clearPendingAddEntries(new ManagedLedgerFencedException(\"ManagedLedger \"","sourceCodeStart":4508,"sourceCodeEnd":4544,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java#L4508-L4544","documentation":"checkManagedLedgerIsOpen() throws this ManagedLedgerException when any synchronous operation is invoked on a managed ledger whose state is Closed. Once closed, a ManagedLedger instance is terminal — it cannot be reopened; a new instance must be obtained from the factory.","triggerScenarios":"Calling addEntry/getEntries/close/delete/etc. after close() completed; a topic whose managed ledger was closed during broker unload but is still referenced by a producer/consumer path; concurrent close racing an in-flight operation.","commonSituations":"Broker unload/partition reassignment closing the ledger while a client operation is mid-flight; topic deletion racing producers; application holding a stale ManagedLedger reference after close.","solutions":["Re-acquire the managed ledger via ManagedLedgerFactory.open()/openLedger after a close — closed instances are not reusable","Check ledger state before operations (isClosed()) in long-lived holders","Fix races by closing/invalidating references to the ledger before calling close()","At the Pulsar-client level, expect this as a topic re-creation signal: reconnect/re-subscribe to get the new ledger"],"exampleFix":"// before\nml.addEntry(data); // throws if closed\n// after\nsynchronized (this) {\n    if (ml.getState() == State.Closed) {\n        ml = factory.open(name);\n    }\n    ml.addEntry(data);\n}","handlingStrategy":"try-catch","validationCode":"if (ml.getState() == State.Closed) {\n    ml = factory.open(ledgerName); // re-acquire\n}","typeGuard":null,"tryCatchPattern":"try {\n    ml.addEntry(data);\n} catch (ManagedLedgerException e) {\n    if (String.valueOf(e.getMessage()).contains(\"has already been closed\")) {\n        reopenLedgerAndRetry();\n    } else { throw e; }\n}","preventionTips":["Drop references to a ledger once close() is called","Serialize close vs in-flight operations with proper synchronization","Handle broker unload by reopening the ledger/topic","Treat closed-ledger as terminal: never operate on the old instance"],"tags":["lifecycle","managed-ledger","closed","state"],"backgroundTag":"resource-already-closed","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}