slackhq/nebula · error
stats.interval was an invalid duration: %s
Error message
stats.interval was an invalid duration: %s
What it means
loadStatsConfig reads stats.interval as a duration when a stats message type other than 'none' is enabled. If the value is missing, non-numeric, or <= 0 after parsing, the config load fails with this message echoing the raw string. Raised during reload/startup before any stats sink is created.
Source
Thrown at stats.go:338
case <-t.C:
for _, fn := range fns {
fn()
}
}
}
}
func loadStatsConfig(c *config.C) (statsConfig, error) {
cfg := statsConfig{
typ: c.GetString("stats.type", ""),
}
if cfg.typ == "" || cfg.typ == "none" {
return cfg, nil
}
cfg.interval = c.GetDuration("stats.interval", 0)
if cfg.interval <= 0 {
return cfg, fmt.Errorf("stats.interval was an invalid duration: %s", c.GetString("stats.interval", ""))
}
switch cfg.typ {
case "graphite":
cfg.graphite.protocol = c.GetString("stats.protocol", "tcp")
cfg.graphite.host = c.GetString("stats.host", "")
if cfg.graphite.host == "" {
return cfg, errors.New("stats.host can not be empty")
}
addr, err := net.ResolveTCPAddr(cfg.graphite.protocol, cfg.graphite.host)
if err != nil {
return cfg, fmt.Errorf("error while setting up graphite sink: %s", err)
}
cfg.graphite.resolvedAddr = addr.String()
cfg.graphite.prefix = c.GetString("stats.prefix", "nebula")
case "prometheus":
cfg.prom.listen = c.GetString("stats.listen", "")
if cfg.prom.listen == "" {View on GitHub (pinned to dd8f660c0a)
Solutions
- Set stats.interval to a valid Go duration with units, e.g. 10s, 1m
- If you don't want metrics, set stats.type: none instead of removing interval
- Ensure the YAML key is under the stats: block and correctly indented
- Use unitless values with Go duration parsing rules only if intended — safest to always include s/m/h
Example fix
// before stats: type: graphite interval: 10 // after stats: type: graphite interval: 10s
Defensive patterns
Strategy: validation
Validate before calling
# Go: validate the duration the same way nebula does before deploying
if d, err := time.ParseDuration(cfg.Stats.Interval); err != nil || d <= 0 {
log.Fatalf("stats.interval must be a positive Go duration like 10s, got %q", cfg.Stats.Interval)
} Try / catch
try {
loadConfig(path)
} catch (e) {
if (e.message.includes("stats.interval was an invalid duration")) {
console.error("set stats.interval with units, e.g. 10s; or use stats.type: none")
}
throw e
} Prevention
- Always write durations with units (10s, 1m) — bare integers fail
- If metrics are disabled, set stats.type: none rather than deleting interval
- Lint nebula configs in CI for required stats keys when any sink type is enabled
When it happens
Trigger: stats.interval is absent, set to a non-duration string ('5', 'every minute', '1m0s typo'), a bare integer without a unit, or an explicit non-positive value while stats.type is graphite or prometheus.
Common situations: Writing interval: 10 instead of 10s in YAML; commenting out interval while keeping stats.type enabled; locale/typo issues like '30sec' instead of '30s'.
Related errors
- stats.host can not be empty
- stats.listen should not be empty
- stats.path should not be empty
- stats.type was not understood: %s
- config `%s` has invalid type: %T
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/abbae5d2e88fe745.
Report an issue: GitHub.