{"record":{"id":"859302bf1632a205","repo":"hyperledger/fabric","slug":"nil-block-859302","errorCode":null,"errorMessage":"nil block","messagePattern":"nil block","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"orderer/consensus/smartbft/util.go","lineNumber":398,"sourceCode":"type nodeConfig struct {\n\tid2Identities NodeIdentitiesByID\n\tremoteNodes   []cluster.RemoteNode\n\tnodeIDs       []uint64\n\tconsenters    []*cb.Consenter\n}\n\n// ConsenterCertificate denotes a TLS certificate of a consenter\ntype ConsenterCertificate struct {\n\tConsenterCertificate []byte\n\tCryptoProvider       bccsp.BCCSP\n}\n\n// IsConsenterOfChannel returns whether the caller is a consenter of a channel\n// by inspecting the given configuration block.\n// It returns nil if true, else returns an error.\nfunc (conCert ConsenterCertificate) IsConsenterOfChannel(configBlock *cb.Block) error {\n\tif configBlock == nil {\n\t\treturn errors.New(\"nil block\")\n\t}\n\tenvelopeConfig, err := protoutil.ExtractEnvelope(configBlock, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, conCert.CryptoProvider)\n\tif err != nil {\n\t\treturn err\n\t}\n\toc, exists := bundle.OrdererConfig()\n\tif !exists {\n\t\treturn errors.New(\"no orderer config in bundle\")\n\t}\n\tif oc.ConsensusType() != \"BFT\" {\n\t\treturn errors.New(\"not a SmartBFT config block\")\n\t}\n\n\tfor _, consenter := range oc.Consenters() {","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/smartbft/util.go#L380-L416","documentation":"ConsenterCertificate.IsConsenterOfChannel inspects a configuration block to decide whether this node's certificate belongs to the channel's consenter set. It explicitly rejects a nil configBlock with this sentinel error, because membership cannot be determined without configuration. Callers typically treat a non-nil return (including this one) as 'not a consenter of the channel'.","triggerScenarios":"IsConsenterOfChannel(nil) — called (e.g. from orderer chain-selection / server code) when no config block was found for the chain, such as an empty ledger, chain not yet started, or lookup returning nil for the channel's latest config block.","commonSituations":"Orderer asked to serve/validate a channel whose ledger has no blocks yet; channel name typo so no config block is retrievable; ledger storage empty or not initialized for that channelID; startup ordering where the chain manager queries before the genesis block is written.","solutions":["Verify the channel name/ID used to look up the config block is correct and the channel actually exists on this orderer","Ensure the ledger for the channel contains at least the genesis/config block; if not, fetch/join the channel or create it","Check for storage corruption or an uninitialized fileLedger directory for that channel and re-initialize from a healthy node","At the call site, treat this error as 'not a consenter of this channel' and skip, as designed by the function contract"],"exampleFix":"// before\nvar configBlock *cb.Block\nerr := conCert.IsConsenterOfChannel(configBlock) // panic-prone/nil sentinel\n\n// after\nclassifier := mgr.GetChain(chainID)\nconfigBlock := ledger(configID).GetConfigBlock() // ensure non-nil via ledger lookup\nif configBlock == nil {\n    return nil // not a consenter; no config exists yet\n}\nerr := conCert.IsConsenterOfChannel(configBlock)","handlingStrategy":"type-guard","validationCode":"configBlock := ledgerInstance.GetConfigBlock(chainID)\nif configBlock == nil {\n    // channel has no config yet; skip consenter check instead of calling IsConsenterOfChannel\n    return nil\n}","typeGuard":"func hasConfigBlock(block *cb.Block) bool {\n    return block != nil && block.Header != nil && len(block.Data.GetData()) > 0\n}\n\n// usage:\n// if !hasConfigBlock(configBlock) { /* not a consenter / channel not initialized */ }","tryCatchPattern":"if err := conCert.IsConsenterOfChannel(configBlock); err != nil {\n    if err.Error() == \"nil block\" {\n        // no config available for this channel; treat as not-a-consenter and continue chain selection\n        return\n    }\n    // other errors: envelope extraction / bundle failures — log for diagnosis\n    logger.Warnf(\"consenter channel check failed: %v\", err)\n}","preventionTips":["Never call IsConsenterOfChannel with a block from a failed ledger lookup; check the lookup error/nil first","Ensure each channel's ledger is initialized with its genesis/config block before chain services query it","Validate channel names against the orderer's ledger list to avoid lookups on nonexistent channels","Treat nil-config situations explicitly in chain selection logic rather than letting sentinel errors carry control flow"],"tags":["hyperledger-fabric","orderer","consenter","nil-check"],"backgroundTag":"nil-config-block","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"}