{"record":{"id":"a1e53476a079c9fd","repo":"hyperledger/fabric","slug":"none-of-heartbeattick-d-electiontick-d-and","errorCode":null,"errorMessage":"none of HeartbeatTick (%d), ElectionTick (%d) and MaxInflightBlocks (%d) can be zero","messagePattern":"none of HeartbeatTick \\((.+?)\\), ElectionTick \\((.+?)\\) and MaxInflightBlocks \\((.+?)\\) can be zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":221,"sourceCode":"\n// VerifyConfigMetadata validates Raft config metadata.\n// Note: ignores certificates expiration.\nfunc VerifyConfigMetadata(metadata *etcdraft.ConfigMetadata, verifyOpts x509.VerifyOptions) error {\n\tif metadata == nil {\n\t\t// defensive check. this should not happen as CheckConfigMetadata\n\t\t// should always be called with non-nil config metadata\n\t\treturn errors.Errorf(\"nil Raft config metadata\")\n\t}\n\n\tif metadata.GetOptions() == nil {\n\t\treturn errors.Errorf(\"nil Raft config metadata options\")\n\t}\n\n\tif metadata.GetOptions().GetHeartbeatTick() == 0 ||\n\t\tmetadata.GetOptions().GetElectionTick() == 0 ||\n\t\tmetadata.GetOptions().GetMaxInflightBlocks() == 0 {\n\t\t// if SnapshotIntervalSize is zero, DefaultSnapshotIntervalSize is used\n\t\treturn errors.Errorf(\"none of HeartbeatTick (%d), ElectionTick (%d) and MaxInflightBlocks (%d) can be zero\",\n\t\t\tmetadata.GetOptions().GetHeartbeatTick(), metadata.GetOptions().GetElectionTick(), metadata.GetOptions().GetMaxInflightBlocks())\n\t}\n\n\t// check Raft options\n\tif metadata.GetOptions().GetElectionTick() <= metadata.GetOptions().GetHeartbeatTick() {\n\t\treturn errors.Errorf(\"ElectionTick (%d) must be greater than HeartbeatTick (%d)\",\n\t\t\tmetadata.GetOptions().GetElectionTick(), metadata.GetOptions().GetHeartbeatTick())\n\t}\n\n\tif d, err := time.ParseDuration(metadata.GetOptions().GetTickInterval()); err != nil {\n\t\treturn errors.Errorf(\"failed to parse TickInterval (%s) to time duration: %s\", metadata.GetOptions().GetTickInterval(), err)\n\t} else if d == 0 {\n\t\treturn errors.Errorf(\"TickInterval cannot be zero\")\n\t}\n\n\tif len(metadata.GetConsenters()) == 0 {\n\t\treturn errors.Errorf(\"empty consenter set\")\n\t}","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L203-L239","documentation":"Raft timing relies on HeartbeatTick, ElectionTick and MaxInflightBlocks all being non-zero; a zero value would break leader election or flow control. VerifyConfigMetadata reports the current values of all three whenever any of them is zero.","triggerScenarios":"A channel config update sets ConsensusType metadata with any of Options.HeartbeatTick, Options.ElectionTick or Options.MaxInflightBlocks equal to 0.","commonSituations":"Typo in configtx.yaml consensus options; copying a template and deleting a field; constructing Options in Go code leaving fields at their zero value; partial migration of config between channels.","solutions":["Set all three fields to positive values (e.g. HeartbeatTick: 1, ElectionTick: 10, MaxInflightBlocks: 256) in the config metadata.","Validate the metadata locally with VerifyConfigMetadata before submitting the config update.","Re-run configtxgen with correct Raft profile settings and inspect the produced configtx.yaml."],"exampleFix":"// before\nOptions{HeartbeatTick: 0, ElectionTick: 10, MaxInflightBlocks: 256}\n// after\nOptions{HeartbeatTick: 1, ElectionTick: 10, MaxInflightBlocks: 256}","handlingStrategy":"validation","validationCode":"o := metadata.GetOptions()\nif o.GetHeartbeatTick() == 0 || o.GetElectionTick() == 0 || o.GetMaxInflightBlocks() == 0 {\n\treturn errors.New(\"raft tick/inflight options must be positive\")\n}","typeGuard":null,"tryCatchPattern":"if err := VerifyConfigMetadata(meta, opts); err != nil {\n\tif strings.Contains(err.Error(), \"can be zero\") {\n\t\t// parse reported values from message and surface them to the operator\n\t\treturn fmt.Errorf(\"fix raft options in channel config: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Use Fabric defaults (HeartbeatTick 1, ElectionTick 10, MaxInflightBlocks 256) unless tuned deliberately.","Run VerifyConfigMetadata in CI against generated channel configs.","Never zero out numeric raft options to 'disable' behavior."],"tags":["hyperledger-fabric","raft","config","validation"],"backgroundTag":"raft-tick-zero-invalid","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"}