Hmbown/CodeWhale · warning

invalid duration component

Error message

invalid duration component: {num_buf:?}

What it means

parse_duration_secs parses human-friendly duration strings like "1d2h30m". When it accumulates digits and hits a unit character, it tries to parse the accumulated buffer as i64. If the buffer is empty (e.g. "dm") or contains only non-numeric leftover characters, this error is thrown naming the offending buffer with {:?}.

Solutions

  1. Fix the duration string so every unit (d/h/m/s) is preceded by digits, e.g. "1d2h30m".
  2. Remove stray non-numeric characters between number and unit.
  3. If you meant an absolute timestamp, use the timestamp form of --since instead of a duration.
  4. Quote the value in your shell so spaces do not split the argument.

Example fix

// before
--since 1d 2h   // buffer becomes empty/garbage between tokens
// after
--since 1d2h
Defensive patterns

Strategy: validation

Validate before calling

// regex pre-check for well-formed duration
const ok = /^(\d+[dhms])+$/.test(value);

Try / catch

try {
  let secs = parseSince(raw);
} catch (e) {
  if (String(e).includes('invalid duration component')) {
    console.error(`Duration must look like 1d2h30m: ${raw}`);
  }
}

Prevention

When it happens

Trigger: A --since/-since duration value contains a unit character not preceded by a number, e.g. "h30", "1d2m..s", or stray letters like "1dxh" that leave an invalid buffer such as "x".

Common situations: Typo in a CLI --since flag ("since=2hh", "since=1d 2h" with a space producing an empty buffer), or passing a timestamp string where a duration was expected.

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 Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/23a5bf9a3882af6c. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/metrics.rs:109

pub fn parse_since(s: &str) -> Result<DateTime<Utc>> {
    let s = s.trim().to_ascii_lowercase();
    let s = s.strip_prefix("now-").unwrap_or(&s);
    let secs = parse_duration_secs(s)?;
    Ok(Utc::now() - Duration::seconds(secs))
}

fn parse_duration_secs(s: &str) -> Result<i64> {
    // Walk through the string accumulating numbers and consuming unit suffixes.
    let mut total: i64 = 0;
    let mut num_buf = String::new();

    for ch in s.chars() {
        match ch {
            '0'..='9' => num_buf.push(ch),
            'd' | 'h' | 'm' | 's' => {
                let n: i64 = num_buf
                    .parse()
                    .map_err(|_| anyhow::anyhow!("invalid duration component: {num_buf:?}"))?;
                num_buf.clear();
                let factor = match ch {
                    'd' => 86_400,
                    'h' => 3_600,
                    'm' => 60,
                    's' => 1,
                    _ => unreachable!(),
                };
                total += n * factor;
            }
            _ => anyhow::bail!("unrecognised character {ch:?} in duration {s:?}"),
        }
    }

    if !num_buf.is_empty() {
        // Trailing bare number — treat as seconds.
        let n: i64 = num_buf.parse()?;
        total += n;

View on GitHub (pinned to 73e0f67d83)