{"record":{"id":"9bf9dbcd1f619dc6","repo":"hyperledger/fabric","slug":"without-a-system-channel-a-follower-should-have-b-9bf9db","errorCode":null,"errorMessage":"without a system channel, a follower should have been created","messagePattern":"without a system channel, a follower should have been created","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/smartbft/consenter.go","lineNumber":186,"sourceCode":"\t}\n\tif smartBFTChain, isBFTSmart := cs.Chain.(*BFTChain); isBFTSmart {\n\t\treturn smartBFTChain\n\t}\n\tc.Logger.Warningf(\"Chain %s is of type %v and not smartbft.Chain\", channelID, reflect.TypeOf(cs.Chain))\n\treturn nil\n}\n\n// HandleChain returns a new Chain instance or an error upon failure\nfunc (c *Consenter) HandleChain(support consensus.ConsenterSupport, metadata *cb.Metadata) (consensus.Chain, error) {\n\tconsenters := support.SharedConfig().Consenters()\n\tconfigOptions, err := createSmartBftConfig(support.SharedConfig())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tselfID, err := c.detectSelfID(consenters)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"without a system channel, a follower should have been created\")\n\t}\n\tc.Logger.Infof(\"Local consenter id is %d\", selfID)\n\n\tconfig, err := util.ConfigFromMetadataOptions(uint64(selfID), configOptions)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed parsing smartbft configuration\")\n\t}\n\tc.Logger.Debugf(\"SmartBFT-Go config: %+v\", config)\n\n\tconfigValidator := &ConfigBlockValidator{\n\t\tValidatingChannel:    support.ChannelID(),\n\t\tFilters:              c.Registrar,\n\t\tConfigUpdateProposer: c.Registrar,\n\t\tLogger:               c.Logger,\n\t}\n\n\tegressCommFactory := func(runtimeConfig *atomic.Value, channelId string, comm cluster.Communicator) EgressComm {\n\t\tchannelDecorator := zap.String(\"channel\", channelId)","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/smartbft/consenter.go#L168-L204","documentation":"In HandleChain, when the orderer has no system channel, a chain must start as a follower that discovers its identity from the channel's config block; only then can detectSelfID map the local TLS/identity cert to a consenter ID. If detectSelfID fails, this wrapper error is returned, indicating the follower/chain-bootstrapping path did not produce a recognizable local consenter certificate in the channel config.","triggerScenarios":"Channel participation mode (no system channel) where HandleChain is invoked for a channel whose most recent config block's Orderer.ConsenterMapping does not contain a consenter whose server/cluster TLS certs match the local node's certificates, or detectSelfID fails on invalid PEM/TLS material.","commonSituations":"Using `osnadmin channel join` with TLS certificates on disk that don't match the certs enrolled in the channel config; joining a channel before the node's certs were added to the consenters list; misconfigured General.TLS / Cluster settings in orderer.yaml; stale join block after cert rotation.","solutions":["Verify the orderer's General.TLS.Certificate and Cluster.ServerCertificate match a consenter entry (client_cert/server_cert) in the channel's Orderer.ConsenterMapping","Re-issue the channel join with the correct latest config block: osnadmin channel join --channel-id ... --config-block <latest>","Rotate or re-enroll the orderer's TLS/identity certs so they match the channel config, then restart the orderer","Check detectSelfID's underlying error in the wrapped message for which cert (server/cluster/TLS) failed to match"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before joining, confirm local TLS certs appear in the channel's consenter mapping\nblock := fetchLatestConfigBlock(channelID)\nconsenters := extractConsenterMapping(block)\nlocal := loadCert(ordererTLSPath)\nif !containsCert(consenters, local) {\n    return fmt.Errorf(\"local TLS cert %s not in channel ConsenterMapping; update channel config first\", ordererTLSPath)\n}","typeGuard":null,"tryCatchPattern":"// HandleChain failure\nchain, err := consenter.HandleChain(support, nil)\nif err != nil {\n    if strings.Contains(err.Error(), \"follower should have been created\") {\n        log.Fatalf(\"local certs not in channel config: %v\", err) // fix certs/block, don't retry blindly\n    }\n    return err\n}","preventionTips":["Add the orderer's certs to the channel ConsenterMapping before joining","Always join with the newest config block after cert rotations","Keep General.TLS and Cluster certs identical and consistent across the cluster","Validate the block with configtxlator before osnadmin channel join"],"tags":["fabric","smartbft","channel-join","tls","certificates"],"backgroundTag":"consenter-cert-not-found","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"}