kopia/kopia · error

invalid duration

Error message

invalid duration %s: %w

What it means

jsonencoding.Duration.UnmarshalText (jsonencoding.go:34) first tries to interpret the text as a plain float of nanoseconds; if that fails it falls back to time.ParseDuration. When both fail, the raw value is wrapped as "invalid duration %s". Any JSON/text field using this Duration type must contain a Go-style duration string or a number of nanoseconds.

Solutions

  1. Use Go duration syntax with units: "300ms", "1h30m", "24h", "720h" for 30 days.
  2. Or provide a plain number interpreted as nanoseconds (e.g. 5000000).
  3. Convert unsupported units manually (weeks→168h, days→24h) in the config.
  4. Pre-validate the string with time.ParseDuration before writing it into the config.

Example fix

// before
{"interval": "1 day"}
// after
{"interval": "24h"}
Defensive patterns

Strategy: validation

Validate before calling

func parseDur(s string) error {
    if _, err := time.ParseDuration(s); err != nil {
        if _, ferr := strconv.ParseFloat(s, 64); ferr != nil {
            return fmt.Errorf("invalid duration %q", s)
        }
    }
    return nil
}

Try / catch

if err := json.Unmarshal(data, &cfg); err != nil {
    var durErr error
    if strings.Contains(err.Error(), "invalid duration") {
        return fmt.Errorf("check duration fields (use Go syntax like 24h): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Unmarshaling JSON/YAML into jsonencoding.Duration with values like "1 hour", "1h30m", "5 mins", "daily", or "" — strings time.ParseDuration cannot parse (no unit, unsupported unit, embedded spaces).

Common situations: Hand-edited config files writing human-friendly durations ("30 days", "1 week"); cron-like strings; locale-formatted numbers with thousand separators; empty values.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/1a1151eec3107561. Report an issue: GitHub.

Appendix: source

Thrown at repo/jsonencoding/jsonencoding.go:34

// MarshalText writes d as text.
func (d Duration) MarshalText() ([]byte, error) {
	return []byte(d.String()), nil
}

// UnmarshalText read d from a text representation.
func (d *Duration) UnmarshalText(b []byte) error {
	s := string(bytes.TrimSpace(b))

	f, err := strconv.ParseFloat(s, 64)
	if err == nil {
		d.Duration = time.Duration(f)

		return nil
	}

	d.Duration, err = time.ParseDuration(s)
	if err != nil {
		return fmt.Errorf("invalid duration %s: %w", s, err)
	}

	return nil
}

View on GitHub (pinned to 82495e54b5)