{"record":{"id":"6b44a458f97ae719","repo":"hyperledger/fabric","slug":"failed-to-parse-tickinterval-s-to-time-duration","errorCode":null,"errorMessage":"failed to parse TickInterval (%s) to time duration","messagePattern":"failed to parse TickInterval \\((.+?)\\) to time duration","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"orderer/consensus/etcdraft/consenter.go","lineNumber":167,"sourceCode":"\t\treturn nil, errors.Wrap(err, \"without a system channel, a follower should have been created\")\n\t}\n\n\tvar evictionSuspicion time.Duration\n\tif c.EtcdRaftConfig.EvictionSuspicion == \"\" {\n\t\tc.Logger.Infof(\"EvictionSuspicion not set, defaulting to %v\", DefaultEvictionSuspicion)\n\t\tevictionSuspicion = DefaultEvictionSuspicion\n\t} else {\n\t\tevictionSuspicion, err = time.ParseDuration(c.EtcdRaftConfig.EvictionSuspicion)\n\t\tif err != nil {\n\t\t\tc.Logger.Panicf(\"Failed parsing Consensus.EvictionSuspicion: %s: %v\", c.EtcdRaftConfig.EvictionSuspicion, err)\n\t\t}\n\t}\n\n\tvar tickInterval time.Duration\n\tif c.EtcdRaftConfig.TickIntervalOverride == \"\" {\n\t\ttickInterval, err = time.ParseDuration(m.GetOptions().GetTickInterval())\n\t\tif err != nil {\n\t\t\treturn nil, errors.Errorf(\"failed to parse TickInterval (%s) to time duration\", m.GetOptions().GetTickInterval())\n\t\t}\n\t} else {\n\t\ttickInterval, err = time.ParseDuration(c.EtcdRaftConfig.TickIntervalOverride)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithMessage(err, \"failed parsing Consensus.TickIntervalOverride\")\n\t\t}\n\t\tc.Logger.Infof(\"TickIntervalOverride is set, overriding channel configuration tick interval to %v\", tickInterval)\n\t}\n\n\topts := Options{\n\t\tRPCTimeout:    c.OrdererConfig.General.Cluster.RPCTimeout,\n\t\tRaftID:        id,\n\t\tClock:         clock.NewClock(),\n\t\tMemoryStorage: raft.NewMemoryStorage(),\n\t\tLogger:        c.Logger,\n\n\t\tTickInterval:         tickInterval,\n\t\tElectionTick:         int(m.GetOptions().GetElectionTick()),","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/consenter.go#L149-L185","documentation":"HandleChain (orderer/consensus/etcdraft/consenter.go:167) parses the TickInterval string from the channel's etcdraft Options into a time.Duration when no TickIntervalOverride is set in the orderer's local config. If the string is not a valid Go duration (e.g. '500' without unit), this error is returned and the chain cannot start. The value originates from channel consensus metadata, so bad metadata breaks all orderers on that channel.","triggerScenarios":"Channel consensus metadata Options.TickInterval is a string that time.ParseDuration cannot parse (missing time unit, empty string, or non-numeric), while EtcdRaftConfig.TickIntervalOverride is unset.","commonSituations":"Writing TickInterval: \"500\" or \"500ms abc\" in the raft Options of a custom config; hand-editing metadata and dropping the 'ms'/'s' suffix; empty TickInterval in a migrated config.","solutions":["Fix Options.TickInterval in the channel config to a valid duration string like \"500ms\" and resubmit via config update (or regenerate genesis)","Alternatively set Orderer.EtcdRaftConfig.TickIntervalOverride (e.g. General.Consensus etcdraft TickInterval in orderer.yaml) to a valid duration to bypass the metadata value","Validate with Go semantics: number plus unit (ns, us, ms, s, m, h) is required"],"exampleFix":"// before (channel config metadata)\nOptions: {TickInterval: \"500\"}\n// after\nOptions: {TickInterval: \"500ms\"} // or in orderer.yaml: TickIntervalOverride: 500ms","handlingStrategy":"validation","validationCode":"if _, err := time.ParseDuration(raftOptions.GetTickInterval()); err != nil {\n    return fmt.Errorf(\"TickInterval %q is not a valid Go duration (e.g. \\\"500ms\\\"): %w\", raftOptions.GetTickInterval(), err)\n}","typeGuard":"func tickIntervalValid(s string) bool {\n    _, err := time.ParseDuration(s)\n    return s != \"\" && err == nil\n}","tryCatchPattern":"chain, err := consenter.HandleChain(support, metadata)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to parse TickInterval\") {\n        // fix Options.TickInterval in channel config or set TickIntervalOverride in orderer.yaml\n    }\n}","preventionTips":["Always include a time unit in TickInterval (e.g. \"500ms\")","Validate raft Options durations with time.ParseDuration in CI before submitting config","Use TickIntervalOverride in orderer.yaml to decouple from channel metadata values"],"tags":["raft","tick-interval","configuration","duration-parse"],"backgroundTag":"invalid-duration-format","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}