matryer/xbar · warning

(from )

Error message

%s (from %s)

What it means

ParseFilenameInterval extracts the interval token from a plugin filename (the dotted segment before the extension) and parses it. If parseInterval fails, the library returns the default interval along with errors.Errorf("%s (from %s)") embedding the inner error and the full filename, so the developer can see which file had the malformed interval.

Solutions

  1. Rename the plugin file so its final dotted segment is a valid interval token: <name>.<N><d|h|m|s|ms>.<ext>, e.g. weather.5m.py
  2. If the plugin should just use the default interval, remove the dotted segment entirely (weather.py) — empty interval yields the default with no error
  3. Handle the returned defaultRefreshInterval when this error occurs, since the interval value is still usable (default 1 minute)

Example fix

// before
iv, err := plugins.ParseFilenameInterval("weather.5min.py") // bad interval value: 5mi...
// after
// rename file to a valid token first: weather.5m.py
iv, err := plugins.ParseFilenameInterval("weather.5m.py") // -> {5, minutes}
if err != nil {
    iv = plugins.RefreshInterval{N: 1, Unit: "minutes"} // accept default
}
Defensive patterns

Strategy: fallback

Validate before calling

var intervalRe = regexp.MustCompile(`\.\d+(ms|[dhms])\.[^.]+$`)
func filenameHasValidInterval(name string) bool {
    return intervalRe.MatchString(strings.TrimSuffix(name, ".stp"))
}

Try / catch

iv, err := plugins.ParseFilenameInterval(filename)
if err != nil {
    log.Printf("using default interval: %v", err)
    iv = plugins.RefreshInterval{N: 1, Unit: "minutes"} // default already returned; safe to ignore
}

Prevention

When it happens

Trigger: Calling plugins.ParseFilenameInterval (directly or via GetInstalledPluginMetadata/NewPlugin) on a filename whose last dotted segment is not a valid interval token, e.g. "weather.5x.py" or "notes..py" producing an empty/suffix parse failure; note filenames with NO interval segment return the default without error.

Common situations: Hand-renamed plugin files ("weather.5m.py" edited to "weather.5min.py"); plugin filenames where the final segment collides with interval parsing, like "my.tool.py" (parses unit 'l' -> bad unit); downloading a plugin renamed by a browser.

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/c651dd8b3b376a61. Report an issue: GitHub.

Appendix: source

Thrown at pkg/plugins/refresh_interval.go:122

		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
	}
	interval, err := parseInterval(intervalStr)
	if err != nil {
		return defaultRefreshInterval, errors.Errorf("%s (from %s)", err.Error(), filename)
	}
	return interval, nil
}

func findIntervalInFilename(filename string) string {
	if filename == "" {
		return ""
	}
	if !IsPluginEnabled(filename) {
		filename = strings.TrimSuffix(filename, disabledPluginExtension)
	}
	fn := filename
	ext := filepath.Ext(filename)
	if ext != "" {
		fn = fn[:len(fn)-len(ext)]
	}
	segs := strings.Split(fn, ".")
	if len(segs) == 1 {

View on GitHub (pinned to d624239058)