{"record":{"id":"ad6318eed599bd3d","repo":"hyperledger/fabric","slug":"communication-has-been-shut-down","errorCode":null,"errorMessage":"communication has been shut down","messagePattern":"communication has been shut down","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"orderer/common/cluster/comm.go","lineNumber":123,"sourceCode":"\n\tstub := mapping.LookupByClientCert(cert)\n\tif stub == nil {\n\t\treturn nil, errors.Errorf(\"certificate extracted from TLS connection isn't authorized\")\n\t}\n\treturn &requestContext{\n\t\tchannel: channel,\n\t\tsender:  stub.ID,\n\t}, nil\n}\n\n// Remote obtains a RemoteContext linked to the destination node on the context\n// of a given channel\nfunc (c *Comm) Remote(channel string, id uint64) (*RemoteContext, error) {\n\tc.Lock.RLock()\n\tdefer c.Lock.RUnlock()\n\n\tif c.shutdown {\n\t\treturn nil, errors.New(\"communication has been shut down\")\n\t}\n\n\tmapping, exists := c.Chan2Members[channel]\n\tif !exists {\n\t\treturn nil, errors.Errorf(\"channel %s doesn't exist\", channel)\n\t}\n\tstub := mapping.ByID(id)\n\tif stub == nil {\n\t\treturn nil, errors.Errorf(\"node %d doesn't exist in channel %s's membership\", id, channel)\n\t}\n\n\tif stub.Active() {\n\t\treturn stub.RemoteContext, nil\n\t}\n\n\terr := stub.Activate(c.createRemoteContext(stub, channel))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/cluster/comm.go#L105-L141","documentation":"Comm.Remote is the public API for obtaining a RemoteContext to talk to a specific cluster node on a channel. If the Comm instance has been shut down (c.shutdown is true, typically after Comm.Stop/Serve termination), all outbound cluster communication is refused with this sentinel error instead of attempting dials on dead infrastructure.","triggerScenarios":"Calling Comm.Remote(channel, id) after c.Stop() or after the Comm's ShutdownC channel has been fired — e.g. an RPC/probe initiated concurrently with orderer shutdown, or reusing a stale Comm reference after the chain was halted.","commonSituations":"Etcdraft chain still issuing requests during graceful orderer shutdown; a long-running component holding a Comm reference past the channel's closure; tests reusing a Comm after Stop without recreating it.","solutions":["Check for shutdown before calling Remote and recreate the Comm if continued operation is needed","Fix lifecycle ordering so chains stop issuing RPCs before Comm.Stop() is invoked","In tests, create a fresh Comm instance after each Stop instead of reusing the shut-down one","Treat this error as terminal (no retry) and propagate cancellation of dependent work"],"exampleFix":"// before (reusing shut-down Comm)\nremote, err := comm.Remote(\"mychannel\", 1)\n// after\nif comm.IsShutdown() {\n    comm = cluster.NewComm(...) // recreate\n}\nremote, err := comm.Remote(\"mychannel\", 1)","handlingStrategy":"try-catch","validationCode":"// Check shutdown flag before using Remote (exposed via channel closure)\nselect {\ncase <-comm.ShutdownC:\n    return errors.New(\"comm shut down; aborting Remote call\")\ndefault:\n    // safe to proceed\n}","typeGuard":"func commIsLive(c *cluster.Comm) bool {\n    select {\n    case <-c.ShutdownC:\n        return false\n    default:\n        return true\n    }\n}","tryCatchPattern":"remote, err := comm.Remote(channel, nodeID)\nif err != nil {\n    if err.Error() == \"communication has been shut down\" {\n        // terminal: cancel work, do not retry on this Comm instance\n        return nil, ErrCommTerminated\n    }\n    return nil, err // transient dial errors may be retried\n}","preventionTips":["Subscribe to the Comm's ShutdownC channel and stop issuing RPCs on closure","Order lifecycle teardown: halt chains/RPC producers before calling Comm.Stop()","Never cache Comm references across channel restarts; obtain them fresh from the manager","In tests, use defer to ensure Remote calls never outlive Stop()"],"tags":["hyperledger-fabric","orderer","lifecycle","shutdown"],"backgroundTag":"service-shutdown","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"}