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
- Include a valid unit suffix on the number: `"300"` → `"300ms"`, `"1 h"` → `"1h"`.
- Remove whitespace and illegal characters; only `[-0-9.]` then `[a-z]` units are accepted.
- Fix malformed numbers like `1.2.3s` to a single decimal `1.23s`.
- 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
- Always pair a number with a unit; never send bare numeric strings.
- Trim and strip whitespace from durations before sending (spaces are rejected).
- Avoid multiple dots or stray signs in the numeric part (e.g. 1.2.3s).
- Share one duration-formatting helper across all Jaeger API calls so the format is consistent.
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
- Invalid time unit: {unit}
- Invalid duration: {num_str}
- Failed to parse duration: {:?}
- node not found in pending
- OTP logs or traces do not support VRL transforms
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/589eee6e070c6716.
Report an issue: GitHub.