{"record":{"id":"7be5831a900198bb","repo":"hyperledger/fabric","slug":"tickinterval-cannot-be-zero","errorCode":null,"errorMessage":"TickInterval cannot be zero","messagePattern":"TickInterval cannot be zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":234,"sourceCode":"\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}\n\t\tif err := validateConsenterTLSCerts(consenter, verifyOpts, true); err != nil {\n\t\t\treturn errors.WithMessagef(err, \"consenter %s:%d has invalid certificate\", consenter.GetHost(), consenter.GetPort())\n\t\t}\n\t}\n\n\tif err := MetadataHasDuplication(metadata); err != nil {\n\t\treturn err","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L216-L252","documentation":"VerifyConfigMetadata guard: Options.TickInterval in the etcdraft ConfigMetadata is set to the zero value. The consensus options must provide a positive tick interval; a missing/zero value is rejected during channel config validation.","triggerScenarios":"Options.TickInterval set to \"0\", \"0s\", \"0ms\", or any string that parses to duration 0.","commonSituations":"Someone tried to 'disable' ticking by zeroing it; generated config leaving TickInterval as \"0s\"; template placeholder never filled in.","solutions":["Set a positive TickInterval, e.g. \"500ms\".","Validate that parsed duration > 0 before submitting the config update.","Check profile in configtx.yaml and regenerate config metadata."],"exampleFix":"// before\nTickInterval: \"0s\"\n// after\nTickInterval: \"500ms\"","handlingStrategy":"validation","validationCode":"d, err := time.ParseDuration(metadata.GetOptions().GetTickInterval())\nif err != nil || d <= 0 {\n\treturn errors.New(\"TickInterval must be a positive duration, e.g. 500ms\")\n}","typeGuard":null,"tryCatchPattern":"if err := VerifyConfigMetadata(meta, opts); err != nil {\n\tif strings.Contains(err.Error(), \"TickInterval cannot be zero\") {\n\t\treturn errors.New(\"configure a positive TickInterval in raft options\")\n\t}\n\treturn err\n}","preventionTips":["Never set TickInterval to 0/0s.","Check both parse success and non-zero duration in pre-validation.","Review generated configs for placeholder values like \"0s\"."],"tags":["hyperledger-fabric","raft","config","validation"],"backgroundTag":"tick-interval-zero","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"}