ThreeMammals/Ocelot · error · FormatException

The ' ' timespan cannot be converted to TimeSpan due to an…

Error message

The '{timespan}' timespan cannot be converted to TimeSpan due to an unknown '{unit}' unit!

What it means

After extracting the numeric value, ParseTimespan maps the unit suffix (d, h, m, s, ms; empty = milliseconds) to a TimeSpan via a switch expression. An unrecognized unit suffix throws FormatException. This protects against typos like '1x' or '5sec'.

Solutions

  1. Change the configured value to an accepted unit: d, h, m, s, ms, or no suffix (milliseconds), e.g. '5min' -> '5m'.
  2. Remember parsing is case-sensitive: use lowercase units.
  3. Add startup validation of rate-limit values so the error names the offending route early.
  4. If you need richer units, extend the switch expression in RateLimitRule.ParseTimespan.

Example fix

// before
"Period": "5min"
// after
"Period": "5m"
Defensive patterns

Strategy: validation

Validate before calling

static readonly string[] Units = { "d", "h", "m", "s", "ms", "" };
bool HasKnownUnit(string p) {
    int i = 0; while (i < p.Length && (char.IsDigit(p[i]) || p[i]=='.')) i++;
    return Units.Contains(p.Substring(i));
}

Try / catch

try { var period = RateLimitRule.PeriodSpan(value); }
catch (FormatException ex) { Error($"Unsupported duration unit in '{value}': {ex.Message}"); throw; }

Prevention

When it happens

Trigger: A Period/Wait config value ends in a suffix outside {d,h,m,s,ms,'' }, e.g. '30sec', '2min', '1hr', '5M' (uppercase M is not accepted as minutes).

Common situations: Human-readable units typed into ocelot.json ('5min'); copy-pasted durations from other libraries; case sensitivity mistakes ('S' vs 's'); localization adding non-standard suffixes.

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 ThreeMammals/Ocelot@d1f22d9304 (2026-09-12). Data as JSON: /api/errors/2fa258cc77e04c46. Report an issue: GitHub.

Appendix: source

Thrown at src/Configuration/RateLimitRule.cs:86

        }

        string val = timespan.Trim();
        int pos = val.Length;
        while (--pos >= 0 && !char.IsDigit(val[pos]) && val[pos] != DecimalSeparator)
        {
        }

        string floating = val[..++pos], unit = val[pos..];
        double value = Math.Abs(double.Parse(floating)); // negative values should be disallowed as they could cause everything to malfunction; TODO: Make sense to have validation in src/Ocelot/Configuration/Validator/RouteFluentValidator
        return unit.ToLower() switch
        {
            "d" => TimeSpan.FromDays(value),
            "h" => TimeSpan.FromHours(value),
            "m" => TimeSpan.FromMinutes(value),
            "s" => TimeSpan.FromSeconds(value),
            "ms" => TimeSpan.FromMilliseconds(value),
            "" => TimeSpan.FromMilliseconds(value), // an unknown unit defaults to milliseconds as the ms unit
            _ => throw new FormatException($"The '{timespan}' timespan cannot be converted to {nameof(TimeSpan)} due to an unknown '{unit}' unit!"),
        };
    }

    private static readonly char DecimalSeparator = new NumberFormatInfo().NumberDecimalSeparator[0];
}

View on GitHub (pinned to d1f22d9304)