{"record":{"id":"a0e4d35db37a45be","repo":"hyperledger/fabric","slug":"electiontick-d-must-be-greater-than-heartbeatti","errorCode":null,"errorMessage":"ElectionTick (%d) must be greater than HeartbeatTick (%d)","messagePattern":"ElectionTick \\((.+?)\\) must be greater than HeartbeatTick \\((.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":227,"sourceCode":"\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}\n\n\t// verifying certificates for being signed by CA, expiration is ignored\n\tfor _, consenter := range metadata.GetConsenters() {\n\t\tif consenter == nil {\n\t\t\treturn errors.Errorf(\"metadata has nil consenter\")\n\t\t}","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L209-L245","documentation":"In etcd/raft, ElectionTick must be strictly greater than HeartbeatTick; otherwise heartbeats could trigger elections or elections would never time out properly. VerifyConfigMetadata enforces this ordering and echoes both values in the message.","triggerScenarios":"A config update sets Options.ElectionTick <= Options.HeartbeatTick (e.g. both 1, or ElectionTick 2 with HeartbeatTick 5).","commonSituations":"Hand-edited channel config where someone tuned ticks and inverted the relationship; copying tick values between profiles; confusion about tick semantics when migrating from Kafka.","solutions":["Set ElectionTick strictly greater than HeartbeatTick (Fabric default: HeartbeatTick 1, ElectionTick 10).","Submit a normal channel config update with corrected Options; note ElectionTick changes require careful liveness consideration.","Pre-validate with VerifyConfigMetadata before submitting the update."],"exampleFix":"// before\nOptions{HeartbeatTick: 10, ElectionTick: 5}\n// after\nOptions{HeartbeatTick: 1, ElectionTick: 10}","handlingStrategy":"validation","validationCode":"o := metadata.GetOptions()\nif o.GetElectionTick() <= o.GetHeartbeatTick() {\n\treturn fmt.Errorf(\"ElectionTick(%d) must exceed HeartbeatTick(%d)\", o.GetElectionTick(), o.GetHeartbeatTick())\n}","typeGuard":null,"tryCatchPattern":"if err := VerifyConfigMetadata(meta, opts); err != nil {\n\tif strings.Contains(err.Error(), \"must be greater than HeartbeatTick\") {\n\t\treturn fmt.Errorf(\"invalid tick ratio in config update: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Keep ElectionTick at least 5-10x HeartbeatTick for stable elections.","Review any manual tuning of tick values before config updates.","Document tick semantics for operators editing configtx.yaml."],"tags":["hyperledger-fabric","raft","config","validation"],"backgroundTag":"election-tick-must-exceed-heartbeat-tick","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"}