{"record":{"id":"e1b3af0c63b20e20","repo":"hyperledger/fabric","slug":"only-one-commit-notifications-channel-is-allowed-a","errorCode":null,"errorMessage":"only one commit notifications channel is allowed at a time","messagePattern":"only one commit notifications channel is allowed at a time","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/ledger/kvledger/kv_ledger.go","lineNumber":968,"sourceCode":"}\n\ntype commitNotifier struct {\n\tdataChannel chan *ledger.CommitNotification\n\tdoneChannel <-chan struct{}\n}\n\n// CommitNotificationsChannel returns a read-only channel on which ledger sends a `CommitNotification`\n// when a block is committed. The CommitNotification contains entries for the transactions from the committed block,\n// which are not malformed, carry a legitimate TxID, and in addition, are not marked as a duplicate transaction.\n// The consumer can close the 'done' channel to signal that the notifications are no longer needed. This will cause the\n// CommitNotifications channel to close. There is expected to be only one consumer at a time. The function returns error\n// if already a CommitNotification channel is active.\nfunc (l *kvLedger) CommitNotificationsChannel(done <-chan struct{}) (<-chan *ledger.CommitNotification, error) {\n\tl.commitNotifierLock.Lock()\n\tdefer l.commitNotifierLock.Unlock()\n\n\tif l.commitNotifier != nil {\n\t\treturn nil, errors.New(\"only one commit notifications channel is allowed at a time\")\n\t}\n\n\tl.commitNotifier = &commitNotifier{\n\t\tdataChannel: make(chan *ledger.CommitNotification, 10),\n\t\tdoneChannel: done,\n\t}\n\n\treturn l.commitNotifier.dataChannel, nil\n}\n\nfunc (l *kvLedger) sendCommitNotification(blockNum uint64, txStatsInfo []*validation.TxStatInfo) {\n\tl.commitNotifierLock.Lock()\n\tdefer l.commitNotifierLock.Unlock()\n\n\tif l.commitNotifier == nil {\n\t\treturn\n\t}\n","sourceCodeStart":950,"sourceCodeEnd":986,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/kvledger/kv_ledger.go#L950-L986","documentation":"CommitNotificationsChannel returns this error when a commit notifications channel already exists on the kvLedger and has not been released. Fabric allows only one active commit notification consumer at a time; the existing notifier must be closed (via the done channel) before a new one can be created.","triggerScenarios":"Calling ledger.CommitNotificationsChannel(done) twice without closing/signalizing the first done channel (or without the first consumer finishing), leaving l.commitNotifier non-nil.","commonSituations":"An application or chaincode event service subscribes twice concurrently (e.g. double initialization, both a block listener and a custom service requesting notifications), or a leaked subscription never closes its done channel.","solutions":["Ensure the previous consumer signals its done channel before subscribing again","Reuse the single existing notifications channel instead of creating a second one","Fan out events internally: one subscriber broadcasts to multiple internal consumers","Restart the peer/service if a notifier leaked due to a bug in an older version"],"exampleFix":"// before: two concurrent subscriptions\nch1, _ := ledger.CommitNotificationsChannel(done1)\nch2, _ := ledger.CommitNotificationsChannel(done2) // error\n// after: one channel fanned out\nch, _ := ledger.CommitNotificationsChannel(done)\ngo func() { for n := range ch { fanout(n) } }()","handlingStrategy":"type-guard","validationCode":"// Guard before subscribing:\nvar mu sync.Mutex\nvar activeCh <-chan *ledger.CommitNotification\nmu.Lock()\nif activeCh != nil { mu.Unlock(); return activeCh, nil } // reuse existing\nmu.Unlock()","typeGuard":"func hasActiveNotifier(ch <-chan *ledger.CommitNotification) bool { return ch != nil }","tryCatchPattern":"ch, err := ledger.CommitNotificationsChannel(done)\nif err != nil && strings.Contains(err.Error(), \"only one commit notifications channel\") {\n    // reuse existing subscription or wait for previous done to close\n    <-previousDone\n    ch, err = ledger.CommitNotificationsChannel(done)\n}","preventionTips":["Maintain a single process-wide commit notification consumer and fan out internally","Always close the done channel when finished consuming","Serialize subscription setup with a mutex in application code"],"tags":["hyperledger-fabric","ledger","concurrency","notifications"],"backgroundTag":"single-consumer-channel-conflict","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"}