hyperledger/fabric · error

only one commit notifications channel is allowed at a time

Error message

only one commit notifications channel is allowed at a time

What it means

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.

Source

Thrown at core/ledger/kvledger/kv_ledger.go:968

}

type commitNotifier struct {
	dataChannel chan *ledger.CommitNotification
	doneChannel <-chan struct{}
}

// CommitNotificationsChannel returns a read-only channel on which ledger sends a `CommitNotification`
// when a block is committed. The CommitNotification contains entries for the transactions from the committed block,
// which are not malformed, carry a legitimate TxID, and in addition, are not marked as a duplicate transaction.
// The consumer can close the 'done' channel to signal that the notifications are no longer needed. This will cause the
// CommitNotifications channel to close. There is expected to be only one consumer at a time. The function returns error
// if already a CommitNotification channel is active.
func (l *kvLedger) CommitNotificationsChannel(done <-chan struct{}) (<-chan *ledger.CommitNotification, error) {
	l.commitNotifierLock.Lock()
	defer l.commitNotifierLock.Unlock()

	if l.commitNotifier != nil {
		return nil, errors.New("only one commit notifications channel is allowed at a time")
	}

	l.commitNotifier = &commitNotifier{
		dataChannel: make(chan *ledger.CommitNotification, 10),
		doneChannel: done,
	}

	return l.commitNotifier.dataChannel, nil
}

func (l *kvLedger) sendCommitNotification(blockNum uint64, txStatsInfo []*validation.TxStatInfo) {
	l.commitNotifierLock.Lock()
	defer l.commitNotifierLock.Unlock()

	if l.commitNotifier == nil {
		return
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure the previous consumer signals its done channel before subscribing again
  2. Reuse the single existing notifications channel instead of creating a second one
  3. Fan out events internally: one subscriber broadcasts to multiple internal consumers
  4. Restart the peer/service if a notifier leaked due to a bug in an older version

Example fix

// before: two concurrent subscriptions
ch1, _ := ledger.CommitNotificationsChannel(done1)
ch2, _ := ledger.CommitNotificationsChannel(done2) // error
// after: one channel fanned out
ch, _ := ledger.CommitNotificationsChannel(done)
go func() { for n := range ch { fanout(n) } }()
Defensive patterns

Strategy: type-guard

Validate before calling

// Guard before subscribing:
var mu sync.Mutex
var activeCh <-chan *ledger.CommitNotification
mu.Lock()
if activeCh != nil { mu.Unlock(); return activeCh, nil } // reuse existing
mu.Unlock()

Type guard

func hasActiveNotifier(ch <-chan *ledger.CommitNotification) bool { return ch != nil }

Try / catch

ch, err := ledger.CommitNotificationsChannel(done)
if err != nil && strings.Contains(err.Error(), "only one commit notifications channel") {
    // reuse existing subscription or wait for previous done to close
    <-previousDone
    ch, err = ledger.CommitNotificationsChannel(done)
}

Prevention

When it happens

Trigger: Calling ledger.CommitNotificationsChannel(done) twice without closing/signalizing the first done channel (or without the first consumer finishing), leaving l.commitNotifier non-nil.

Common situations: 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.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/e1b3af0c63b20e20. Report an issue: GitHub.