hyperledger/fabric · warning

block deliverer for channel `%s` is <nil>, can't stop delive

Error message

block deliverer for channel `%s` is <nil>, can't stop delivery

What it means

StopDeliverForChannel returns this error when d.blockDeliverer is nil, meaning no active block deliverer exists for the channel to stop. This happens if delivery was never started for the channel or a previous stop already cleared the deliverer.

Source

Thrown at core/deliverservice/deliveryclient.go:310

	return dcBFT, nil
}

// StopDeliverForChannel stops blocks delivery for channel by stopping channel block provider
func (d *deliverServiceImpl) StopDeliverForChannel() error {
	d.lock.Lock()
	defer d.lock.Unlock()

	if d.stopping {
		errMsg := fmt.Sprintf("block deliverer for channel `%s` is already stopped", d.channelID)
		logger.Errorf("Delivery service: %s", errMsg)
		return errors.New(errMsg)
	}

	if d.blockDeliverer == nil {
		errMsg := fmt.Sprintf("block deliverer for channel `%s` is <nil>, can't stop delivery", d.channelID)
		logger.Errorf("Delivery service: %s", errMsg)
		return errors.New(errMsg)
	}
	d.blockDeliverer.Stop()
	d.blockDeliverer = nil

	logger.Debugf("This peer will stop passing blocks from orderer service to other peers on channel: %s", d.channelID)
	return nil
}

// Stop all service and release resources
func (d *deliverServiceImpl) Stop() {
	d.lock.Lock()
	defer d.lock.Unlock()
	// Marking flag to indicate the shutdown of the delivery service
	d.stopping = true

	if d.blockDeliverer != nil {
		d.blockDeliverer.Stop()
		d.blockDeliverer = nil

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure StartDeliverForChannel succeeded before calling StopDeliverForChannel for that channel
  2. Treat the error as benign if stop is intended to be idempotent and skip it
  3. Check logs for an earlier start failure (e.g. 'failed to access client TLS configuration') that left the deliverer nil
  4. Fix the channel ID / lifecycle ordering so stop only runs on started channels

Example fix

// before
service.StopDeliverForChannel(channelID) // may fail if never started
// after
if err := service.StopDeliverForChannel(channelID); err != nil &&
    strings.Contains(err.Error(), "is <nil>") {
    logger.Debugf("channel %s was not started; nothing to stop", channelID)
}
Defensive patterns

Strategy: validation

Validate before calling

if !startedChannels[channelID] {
    return fmt.Errorf("channel %s was never started", channelID)
}

Type guard

func isNilDelivererErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "is <nil>")
}

Try / catch

if err := svc.StopDeliverForChannel(ch); err != nil {
    if isNilDelivererErr(err) {
        logger.Debugf("nothing to stop for %s", ch)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling StopDeliverForChannel(channelID) when StartDeliverForChannel was never called for that channel, or after a prior StopDeliverForChannel already set d.blockDeliverer = nil, or if start failed earlier (e.g. error 830).

Common situations: Stopping a channel that failed to start due to a TLS/config error; calling stop on a typo'd or removed channel ID; shutdown paths running after an earlier stop cleared state.

Related errors


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