{"record":{"id":"847b8f7babec27e2","repo":"hyperledger/fabric","slug":"block-deliverer-for-channel-s-already-exists","errorCode":null,"errorMessage":"block deliverer for channel `%s` already exists","messagePattern":"block deliverer for channel `(.+?)` already exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/deliverservice/deliveryclient.go","lineNumber":115,"sourceCode":"\n// StartDeliverForChannel starts blocks delivery for channel\n// initializes the grpc stream for given chainID, creates blocks provider instance\n// that spawns in go routine to read new blocks starting from the position provided by ledger\n// info instance.\nfunc (d *deliverServiceImpl) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {\n\td.lock.Lock()\n\tdefer d.lock.Unlock()\n\n\tif d.stopping {\n\t\terrMsg := fmt.Sprintf(\"block deliverer for channel `%s` is stopping\", chainID)\n\t\tlogger.Errorf(\"Delivery service: %s\", errMsg)\n\t\treturn errors.New(errMsg)\n\t}\n\n\tif d.blockDeliverer != nil {\n\t\terrMsg := fmt.Sprintf(\"block deliverer for channel `%s` already exists\", chainID)\n\t\tlogger.Errorf(\"Delivery service: %s\", errMsg)\n\t\treturn errors.New(errMsg)\n\t}\n\n\t// TODO save the initial bundle in the block deliverer in order to maintain a stand alone BlockVerifier that gets updated\n\t// immediately after a config block is pulled and verified.\n\tbundle, err := channelconfig.NewBundle(chainID, d.conf.ChannelConfig, d.conf.CryptoProvider)\n\tif err != nil {\n\t\treturn errors.WithMessagef(err, \"failed to create block deliverer for channel `%s`\", chainID)\n\t}\n\toc, ok := bundle.OrdererConfig()\n\tif !ok {\n\t\t// This should never happen because it is checked in peer.createChannel()\n\t\treturn errors.Errorf(\"failed to create block deliverer for channel `%s`, missing OrdererConfig\", chainID)\n\t}\n\n\tswitch ct := oc.ConsensusType(); ct {\n\tcase \"etcdraft\":\n\t\td.blockDeliverer, err = d.createBlockDelivererCFT(chainID, ledgerInfo)\n\tcase \"BFT\":","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/deliverservice/deliveryclient.go#L97-L133","documentation":"StartDeliverForChannel returns this when a block deliverer is already registered on the DeliveryClient (d.blockDeliverer != nil), since Fabric's delivery client supports exactly one active deliverer per client instance. A second start request is rejected.","triggerScenarios":"Calling StartDeliverForChannel twice for the same client without StopDeliverForChannel in between; two components (e.g. gossip leader and custom consumer) both starting delivery on one shared client.","commonSituations":"Leader-election flapping causing repeated start calls; application code retrying start after a transient failure without stopping first; sharing a single DeliveryClient across channel services.","solutions":["Call StopDeliverForChannel before attempting to start again on the same client.","Track started state in your code and make StartDeliverForChannel idempotent at the call site.","Create a separate DeliveryClient instance if you need concurrent block delivery consumers.","If caused by leader election retries, add a guard so only the elected leader starts delivery once."],"exampleFix":"// before\nclient.StartDeliverForChannel(chainID, f, stop)\nclient.StartDeliverForChannel(chainID, f2, stop2) // error\n// after\nclient.StartDeliverForChannel(chainID, f, stop)\nclient.StopDeliverForChannel(chainID)\nclient.StartDeliverForChannel(chainID, f2, stop2)","handlingStrategy":"validation","validationCode":"var delivering atomic.Bool\nif !delivering.CompareAndSwap(false, true) {\n    return errors.New(\"delivery already running\")\n}\nerr := client.StartDeliverForChannel(chainID, finalize, stopCh)\nif err != nil {\n    delivering.Store(false)\n}","typeGuard":null,"tryCatchPattern":"if err := client.StartDeliverForChannel(chainID, f, stopCh); err != nil {\n    if strings.Contains(err.Error(), \"already exists\") {\n        logger.Infof(\"deliverer already active for %s\", chainID)\n        return nil\n    }\n    return err\n}","preventionTips":["Stop the existing deliverer (StopDeliverForChannel) before starting a new one.","Keep one deliverer per DeliveryClient; use separate clients for extra consumers.","Make leader-election start calls idempotent/guarded with a flag.","Avoid retrying Start blindly after failures — stop first, then start."],"tags":["delivery-service","lifecycle","duplicate"],"backgroundTag":"resource-already-started","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"}