matryer/xbar · warning
bad interval value: %d
Error message
bad interval value: %d
What it means
validateRefreshInterval rejects a RefreshInterval whose N is less than 1 with errors.Errorf("bad interval value: %d"). SetRefreshInterval calls this before touching the filesystem and wraps it as 'invalid refresh interval'. The library requires a positive interval count because zero/negative durations make no scheduling sense.
Source
Thrown at pkg/plugins/refresh_interval.go:101
_, err = os.Stat(oldVarFullPath)
if err != nil && !os.IsNotExist(err) {
return "", RefreshInterval{}, errors.Wrap(err, "stat plugin vars file")
}
if err != nil && os.IsNotExist(err) {
// no variable file, no probs
return newFilename, refreshInterval, nil
}
newVarFilename := newFilename + variableJSONFileExt
newVarFullPath := filepath.Join(pluginDirectory, newVarFilename)
if err := os.Rename(oldVarFullPath, newVarFullPath); err != nil {
return "", RefreshInterval{}, errors.Wrap(err, "rename plugin vars file to new refresh interval")
}
return newFilename, refreshInterval, nil
}
func validateRefreshInterval(refreshInterval RefreshInterval) error {
if n := refreshInterval.N; n < 1 {
return errors.Errorf("bad interval value: %d", n)
}
for _, unit := range []string{"days", "hours", "minutes", "seconds", "milliseconds"} {
if refreshInterval.Unit == unit {
return nil
}
}
return errors.Errorf("bad interval unit: %s", refreshInterval.Unit)
}
// ParseFilenameInterval parses the filename to extract the refresh interval
// or returns a default if it is do so.
func ParseFilenameInterval(filename string) (RefreshInterval, error) {
// ignore disabled piece
filename = strings.TrimSuffix(filename, disabledPluginExtension)
intervalStr := findIntervalInFilename(filename)
if intervalStr == "" {
return defaultRefreshInterval, nil
}View on GitHub (pinned to d624239058)
Solutions
- Pass N >= 1 with a valid Unit (days/hours/minutes/seconds/milliseconds) to SetRefreshInterval
- Validate the parsed JSON/config before constructing RefreshInterval, rejecting N < 1 at the boundary
- Coerce sub-1 values to the smallest allowed representation (e.g. 500ms as {N:500, Unit:"milliseconds"}) instead of {N:0.5, Unit:"seconds"}
Example fix
// before
iv, _ := strconv.ParseInt(userInput, 10, 64)
err := plugins.SetRefreshInterval(dir, path, plugins.RefreshInterval{N: iv, Unit: "minutes"}) // 0 -> error
// after
iv, err := strconv.ParseInt(userInput, 10, 64)
if err != nil || iv < 1 {
iv = 1
}
_, _, err = plugins.SetRefreshInterval(dir, path, plugins.RefreshInterval{N: iv, Unit: "minutes"}) Defensive patterns
Strategy: validation
Validate before calling
func validInterval(iv plugins.RefreshInterval) bool {
return iv.N >= 1
}
// call: if !validInterval(iv) { iv = plugins.RefreshInterval{N: 1, Unit: "minutes"} } Try / catch
if _, _, err := plugins.SetRefreshInterval(dir, path, iv); err != nil {
if strings.Contains(err.Error(), "bad interval value") {
_, _, err = plugins.SetRefreshInterval(dir, path, plugins.RefreshInterval{N: 1, Unit: "minutes"})
}
} Prevention
- Default zero-valued structs: never pass plugins.RefreshInterval{} straight in — N is 0
- Clamp parsed user input to >= 1 before constructing the interval
- Convert fractional durations to the next smaller unit instead of truncating to 0
When it happens
Trigger: Calling plugins.SetRefreshInterval (or validateRefreshInterval in tests) with plugins.RefreshInterval{N: 0, ...} or {N: -5, ...}. Note the check is n < 1, so N must be >= 1.
Common situations: Unmarshaling an interval from JSON where the field is missing (N defaults to 0); a UI sending an empty/zero value; user typing 0 in a settings field; integer overflow or truncation from a float seconds value of 0.5.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02).
Data as JSON: /api/errors/bbdce4e2dbe28f21.
Report an issue: GitHub.