{"record":{"id":"fd9bcd63f923bec9","repo":"hyperledger/fabric","slug":"ledger-already-opened","errorCode":null,"errorMessage":"ledger already opened","messagePattern":"ledger already opened","errorType":"exception","errorClass":"ErrLedgerAlreadyOpened","httpStatus":null,"severity":"error","filePath":"core/ledger/ledgermgmt/ledger_mgmt.go","lineNumber":29,"sourceCode":"\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/hyperledger/fabric-lib-go/common/flogging\"\n\t\"github.com/hyperledger/fabric-lib-go/common/metrics\"\n\t\"github.com/hyperledger/fabric-protos-go-apiv2/common\"\n\tpb \"github.com/hyperledger/fabric-protos-go-apiv2/peer\"\n\t\"github.com/hyperledger/fabric/core/common/ccprovider\"\n\t\"github.com/hyperledger/fabric/core/ledger\"\n\t\"github.com/hyperledger/fabric/core/ledger/cceventmgmt\"\n\t\"github.com/hyperledger/fabric/core/ledger/kvledger\"\n\t\"github.com/hyperledger/fabric/internal/fileutil\"\n\t\"github.com/pkg/errors\"\n)\n\nvar logger = flogging.MustGetLogger(\"ledgermgmt\")\n\n// ErrLedgerAlreadyOpened is thrown by a CreateLedger call if a ledger with the given id is already opened\nvar ErrLedgerAlreadyOpened = errors.New(\"ledger already opened\")\n\n// ErrLedgerMgmtNotInitialized is thrown when ledger mgmt is used before initializing this\nvar ErrLedgerMgmtNotInitialized = errors.New(\"ledger mgmt should be initialized before using\")\n\n// LedgerMgr manages ledgers for all channels\ntype LedgerMgr struct {\n\tcreationLock         sync.Mutex\n\tjoinBySnapshotStatus *pb.JoinBySnapshotStatus\n\n\tlock           sync.Mutex\n\topenedLedgers  map[string]ledger.PeerLedger\n\tledgerProvider ledger.PeerLedgerProvider\n\n\tebMetadataProvider MetadataProvider\n}\n\ntype MetadataProvider interface {\n\tPackageMetadata(ccid string) ([]byte, error)","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/ledgermgmt/ledger_mgmt.go#L11-L47","documentation":"ErrLedgerAlreadyOpened is a sentinel error returned by LedgerMgr.OpenLedger when the requested channel's ledger is already present in the manager's openedLedgers map, since each ledger may only be opened once per manager instance. It prevents duplicate handles to the same underlying ledger, which would corrupt state coordination.","triggerScenarios":"Calling OpenLedger(id) twice for the same ledger/channel id without closing the first instance; a channel created via CreateLedger and then opened again; test code calling OpenLedger on an already-open ledger.","commonSituations":"Applications caching ledger handles then re-invoking OpenLedger on channel join or reconnection; race between two goroutines opening the same channel; event handlers firing OpenLedger redundantly after the ledger was already opened at creation.","solutions":["Track which ledger ids are already opened and reuse the existing PeerLedger handle instead of calling OpenLedger again.","Compare against the exported sentinel with errors.Is(err, ledgermgmt.ErrLedgerAlreadyOpened) and treat it as 'already have it' rather than a fatal failure.","Call CloseLedger for the open ledger before attempting to reopen it, if reopening is genuinely required."],"exampleFix":"// before\nl, err := ledgerMgr.OpenLedger(chanID)\nif err != nil {\n    return err\n}\n\n// after\nl, err := ledgerMgr.OpenLedger(chanID)\nif err != nil {\n    if errors.Is(err, ledgermgmt.ErrLedgerAlreadyOpened) {\n        l = existingHandles[chanID] // reuse cached handle\n        return nil\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// keep a set of already-opened ledgers\nif _, open := openedLedgers[chanID]; open {\n    return openedLedgers[chanID], nil // skip OpenLedger\n}","typeGuard":"func IsLedgerAlreadyOpened(err error) bool {\n    return errors.Is(err, ledgermgmt.ErrLedgerAlreadyOpened)\n}","tryCatchPattern":"lgr, err := ledgerMgr.OpenLedger(chanID)\nif err != nil {\n    if errors.Is(err, ledgermgmt.ErrLedgerAlreadyOpened) {\n        return cachedLedgerHandle(chanID), nil\n    }\n    return err\n}","preventionTips":["Cache PeerLedger handles per channel instead of reopening","Call CloseLedger before reopening a ledger","Guard concurrent OpenLedger calls for the same channel with a mutex or singleflight","Use errors.Is against the exported sentinel, never string matching"],"tags":["go","hyperledger-fabric","sentinel-error","ledger"],"backgroundTag":"already-open","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}