nautechsystems/nautilus_trader · error · TransportError::Io(std::io::Error)

{field} exceeds the maximum backoff duration

Error message

{field} exceeds the maximum backoff duration

What it means

Retry backoff durations are converted to milliseconds (u64) for scheduling. If the duration's nanosecond value overflows `u64::MAX` (about 584 years), `duration_to_millis` rejects it with `InvalidInput`, naming the offending field, because such a duration cannot be represented for the timer.

Source

Thrown at crates/network/src/websocket/client.rs:963

        })
    } else {
        Ok(RetryConfig {
            max_retries: 0,
            initial_delay_ms: 1,
            max_delay_ms: 1,
            backoff_factor: 1.0,
            jitter_ms: 0,
            operation_timeout_ms: None,
            immediate_first: false,
            max_elapsed_ms: None,
        })
    }
}

fn duration_to_millis(field: &str, duration: Duration) -> Result<u64, TransportError> {
    let nanoseconds = duration.as_nanos();
    if nanoseconds > u128::from(u64::MAX) {
        return Err(TransportError::Io(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            format!("{field} exceeds the maximum backoff duration"),
        )));
    }

    let milliseconds = nanoseconds
        .div_ceil(1_000_000)
        .min(u128::from(u64::MAX / 1_000_000));
    u64::try_from(milliseconds).map_err(|_| {
        TransportError::Io(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            format!("{field} exceeds the maximum backoff duration"),
        ))
    })
}

async fn await_initial_connect_attempt<F, T>(
    cancellation_token: &CancellationToken,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Cap the backoff field to a sane maximum (e.g. `Duration::from_secs(3600)`).
  2. Audit the arithmetic that produces the duration for overflow or wrong units.
  3. Clamp before constructing RetryConfig, mirroring the library's `min(u64::MAX / 1_000_000)` ms bound.

Example fix

// before
let backoff = Duration::from_secs(initial).checked_mul(2u32.pow(n))?;
// after
let backoff = Duration::from_secs(initial).checked_mul(2u32.pow(n))?.min(Duration::from_secs(3600));
Defensive patterns

Strategy: validation

Validate before calling

fn cap_backoff(d: Duration, max: Duration) -> Duration {
    if d > max { max } else { d }
}
let retry = RetryConfig { max_backoff: cap_backoff(computed, Duration::from_secs(3600)), .. };

Try / catch

match RetryConfig::new(initial, factor, max_backoff) {
    Err(e) if e.to_string().contains("exceeds the maximum backoff duration") => {
        eprintln!("backoff duration overflow; capping to 1h");
        RetryConfig::new(initial, factor, Duration::from_secs(3600))?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Constructing `RetryConfig` with a backoff field (e.g. `max_backoff`/initial backoff) computed to an astronomically large `Duration` — typically from a bad multiplication, a u128/u64 mixup, or a parsed value without bounds.

Common situations: Programmatic backoff generation (exponential growth without cap) or deserializing durations from configs with absurd numeric values.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/252ee441be4754ed. Report an issue: GitHub.