ThreeMammals/Ocelot · error · FormatException

The ' ' value doesn't include any digits, so it cannot be…

Error message

The '{timespan}' value doesn't include any digits, so it cannot be considered a number!

What it means

RateLimitRule.ParseTimespan parses rate-limit period strings like '1s', '5m'. If the input string contains no digit characters at all, it throws FormatException because no numeric value can be extracted. The call chain is PeriodSpan/WaitSpan -> ParseTimespan, fed from the RateLimitOptions configuration.

Solutions

  1. Fix the configured value to include a number plus optional unit, e.g. '1s', '5m', '100ms'.
  2. Validate ocelot.json route rate-limit options before startup (RouteFluentValidator per the TODO).
  3. If values come from config providers/env vars, check the substituted value is non-empty and numeric.
  4. Wrap client creation in try/catch for FormatException to surface which route is misconfigured.

Example fix

// before
"RateLimitRule": { "Period": "s", "Limit": 10 }
// after
"RateLimitRule": { "Period": "1s", "Limit": 10 }
Defensive patterns

Strategy: validation

Validate before calling

bool IsValidRateLimitPeriod(string p) =>
    !string.IsNullOrEmpty(p) && p.Any(char.IsDigit);
// run before loading ocelot.json rate-limit rules

Try / catch

try { var period = RateLimitRule.PeriodSpan(rule.Period); }
catch (FormatException ex) { Error($"Bad rate-limit period '{rule.Period}': {ex.Message}"); throw; }

Prevention

When it happens

Trigger: A route's RateLimitRule Period or a client's Wait value is set to a string like 's', 'abc', '', or '.m' — no digits anywhere in the value.

Common situations: Typo in ocelot.json rate limiting config ('Period': 'second'); empty string from environment-variable substitution; deserialized default leaving a blank period.

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

Appendix: source

Thrown at src/Configuration/RateLimitRule.cs:67

    /// <summary>
    /// Parses a timespan string, such as "1ms", "1s", "1m", "1h", "1d".
    /// </summary>
    /// <remarks>Converts a string to milliseconds when the unit is missing or undefined, automatically applying the 'ms' unit.</remarks>
    /// <param name="timespan">The string value with units: '1ms', '1s', '1m', '1h', '1d'.</param>
    /// <returns>A <see cref="TimeSpan"/> value.</returns>
    /// <exception cref="FormatException">If the value is not a number, or the unit of value cannot be determined.</exception>
    public static TimeSpan ParseTimespan(string timespan)
    {
        if (string.IsNullOrWhiteSpace(timespan))
        {
            return TimeSpan.Zero;
        }

        if (!timespan.Any(char.IsDigit)) 
        {
            // TODO: Make sense to have validation in src/Ocelot/Configuration/Validator/RouteFluentValidator
            throw new FormatException($"The '{timespan}' value doesn't include any digits, so it cannot be considered a number!");
        }

        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

View on GitHub (pinned to d1f22d9304)