hyperledger/fabric · warning
block deliverer for channel `%s` is already stopped
Error message
block deliverer for channel `%s` is already stopped
What it means
StopDeliverForChannel returns this error when the channel's block deliverer has already entered the stopping state (d.stopping is true under the service lock). It is a guard against double-stop of the delivery service for a channel, not a malfunction.
Source
Thrown at core/deliverservice/deliveryclient.go:304
return nil, fmt.Errorf("failed to access client TLS configuration: %w", err)
}
dcBFT.TLSCertHash = util.ComputeSHA256(cert.Certificate[0])
}
dcBFT.Initialize(d.conf.ChannelConfig, "")
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()View on GitHub (pinned to 2736b63f8f)
Solutions
- Treat this error as benign and idempotent — check for the 'already stopped' message and ignore it
- Track stop state on the caller side so StopDeliverForChannel is invoked once per channel
- Synchronize concurrent stop calls so only one goroutine stops a given channel
- If the channel should be usable again, restart delivery via StartDeliverForChannel instead of re-stopping
Example fix
// before
err := deliverService.StopDeliverForChannel(ch)
if err != nil { return err }
// after
err := deliverService.StopDeliverForChannel(ch)
if err != nil && strings.Contains(err.Error(), "already stopped") {
return nil // idempotent stop
}
if err != nil { return err } Defensive patterns
Strategy: try-catch
Validate before calling
// check state before calling stop, if exposed by your wrapper
if tracker.IsStopping(channelID) {
return nil
} Type guard
func isAlreadyStopped(err error) bool {
return err != nil && strings.Contains(err.Error(), "is already stopped")
} Try / catch
if err := svc.StopDeliverForChannel(ch); err != nil {
if isAlreadyStopped(err) {
return nil // idempotent
}
return err
} Prevention
- Make stop operations idempotent in your orchestration code
- Guard with a per-channel mutex around start/stop calls
- Invoke stop from a single shutdown path, not multiple hooks
When it happens
Trigger: Calling StopDeliverForChannel(channelID) twice for the same channel, or calling it after Stop()/stopping was already initiated (e.g. during peer shutdown or previous stop attempt).
Common situations: Application or lifecycle code invoking stop-delivery in both a shutdown hook and an error-handling path; reconnection logic stopping a channel that the service is concurrently stopping; duplicate channel close events.
Related errors
- failed listing installed chaincodes
- failed to parse collection config
- [channel %s] failed to get chaincode container info for %s
- chaincode '%s' does not require initialization but called as
- private data APIs are not allowed in chaincode Init()
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/10a8f6758b44c914.
Report an issue: GitHub.