quickwit-oss/quickwit · warning

Invalid duration string

Error message

Invalid duration string

What it means

`parse_duration_nanos` walks the input characters: digits, `.`, and `-` accumulate the numeric part; the first alphabetic character starts the unit suffix. Any non-digit, non-`.`/`-`, non-alphabetic character (space in the middle, `+`, `_`, etc.), or a string with no alphabetic unit at all (e.g. `"100"` or `"abc"`), falls through to `bail!("Invalid duration string")`. Both the mid-loop and end-of-input paths use this same generic message.

Source

Thrown at quickwit/quickwit-serve/src/jaeger_api/parse_duration.rs:62

        }
        if ch.is_alphabetic() {
            let unit = &input[num_str.len()..];
            let num: f64 = num_str.parse()?;
            let duration: f64 = match unit {
                "ns" => num,
                "us" | "µs" => num * 1000.0,
                "ms" => num * 1_000_000.0,
                "s" => num * 1_000_000_000.0,
                "m" => num * 60.0 * 1_000_000_000.0,
                "h" => num * 3600.0 * 1_000_000_000.0,
                _ => anyhow::bail!("Invalid time unit: {}", unit),
            };
            if num < i64::MIN as f64 || num > i64::MAX as f64 {
                anyhow::bail!("Invalid duration: {}", num_str)
            }
            return Ok(duration.round() as i64);
        } else {
            anyhow::bail!("Invalid duration string")
        }
    }
    anyhow::bail!("Invalid duration string")
}

#[cfg(test)]
mod tests {
    use crate::jaeger_api::parse_duration::parse_duration_nanos;

    #[test]
    fn test_parse_duration_nanos() {
        // Test valid duration strings
        assert_eq!(parse_duration_nanos("300ns").unwrap(), 300);
        assert_eq!(parse_duration_nanos("1us").unwrap(), 1000);
        assert_eq!(parse_duration_nanos("2.5ms").unwrap(), 2500000);
        assert_eq!(parse_duration_nanos("3s").unwrap(), 3000000000);
        assert_eq!(parse_duration_nanos("4m").unwrap(), 240000000000);
        assert_eq!(parse_duration_nanos("5h").unwrap(), 18000000000000);

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Include a valid unit suffix on the number: `"300"` → `"300ms"`, `"1 h"` → `"1h"`.
  2. Remove whitespace and illegal characters; only `[-0-9.]` then `[a-z]` units are accepted.
  3. Fix malformed numbers like `1.2.3s` to a single decimal `1.23s`.
  4. Validate/normalize duration strings on the client before issuing Jaeger API requests; if the input is a plain number, decide the intended unit explicitly.

Example fix

// before
lookback=1%20h   // "1 h"

// after
lookback=1h
Defensive patterns

Strategy: validation

Validate before calling

let re = regex::Regex::new(r"^-?\d+(\.\d+)?(ns|us|µs|ms|s|m|h)$").unwrap();
if !re.is_match(duration_str.trim()) {
    return Err(format!("malformed duration '{}': expected e.g. 300ms, -1.5h", duration_str));
}

Try / catch

match parse_duration_with_units(input) {
    Ok(d) => use_duration(d),
    Err(e) if e.to_string().contains("Invalid duration string") => {
        eprintln!("'{}' is not <number><unit> (e.g. 300ms); stripping whitespace/unitless values", input);
        normalize_then_retry(input)
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Sending the Jaeger API a duration string that is not `<number><unit>`: a bare number like `"300"` (missing unit), a string like `"1.2.3s"` (double dot breaks f64 parse), `"abc"` (no leading digits, so the first char is alphabetic but unit match fails / num parse fails), or containing spaces/`+` such as `"5 min"`.

Common situations: Query params with URL-decoded spaces (`lookback=1%20h`); clients omitting the unit entirely (Go `time.Duration` users sending nanosecond ints as strings); typo'd values like `"1-.23s"`; empty strings reaching the parser.

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 quickwit-oss/quickwit@a39730c5cd (2026-09-08). Data as JSON: /api/errors/589eee6e070c6716. Report an issue: GitHub.