nautechsystems/nautilus_trader · error · anyhow::Error

Invalid time range: start={start:?} end={end:?}

Error message

Invalid time range: start={start:?} end={end:?}

What it means

request_reports (bulk order history query) validates that when both start and end timestamps are provided, start must be strictly before end. anyhow::ensure! raises this error otherwise. It guards against sending BitMEX a reversed or zero-width time range that would return no useful data.

Source

Thrown at crates/adapters/bitmex/src/http/client.rs:2123

    /// Request multiple order status reports.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - Credentials are missing.
    /// - The request fails.
    /// - The API returns an error.
    pub async fn request_order_status_reports(
        &self,
        instrument_id: Option<InstrumentId>,
        open_only: bool,
        start: Option<Timestamp>,
        end: Option<Timestamp>,
        limit: Option<u32>,
    ) -> anyhow::Result<Vec<OrderStatusReport>> {
        if let (Some(start), Some(end)) = (start, end) {
            anyhow::ensure!(
                start < end,
                "Invalid time range: start={start:?} end={end:?}",
            );
        }

        let mut params = GetOrderParamsBuilder::default();

        if let Some(instrument_id) = &instrument_id {
            params.symbol(instrument_id.symbol.as_str());
        }

        if open_only {
            params.filter(serde_json::json!({
                "open": true
            }));
        }

        if let Some(start) = start {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Swap start and end so start < end before calling, or normalize with min/max.
  2. Make the end exclusive and advance it by at least one nanosecond past start for zero-width requests.
  3. Validate the configured reporting window at load time and fail fast with a clear config error.
  4. If only one bound is meaningful, pass None for the other instead of a degenerate pair.

Example fix

// before
let (start, end) = (end_ts, start_ts); // accidentally swapped
client.request_reports(instrument_id, open_only, Some(start), Some(end), limit).await?;
// after
let (lo, hi) = if start_ts <= end_ts { (start_ts, end_ts) } else { (end_ts, start_ts) };
client.request_reports(instrument_id, open_only, Some(lo), Some(hi), limit).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_range(start: Option<Timestamp>, end: Option<Timestamp>) -> bool {
    match (start, end) {
        (Some(s), Some(e)) => s < e,
        _ => true,
    }
}

Type guard

fn normalize_range(start: Timestamp, end: Timestamp) -> (Timestamp, Timestamp) {
    if start <= end { (start, end) } else { (end, start) }
}

Try / catch

if !valid_range(start, end) {
    return Err(anyhow::anyhow!("report window: start must be before end"));
}
let reports = client.request_reports(instrument_id, open_only, start, end, limit).await?;

Prevention

When it happens

Trigger: Calling the bulk order report request with start == end (zero-width window), or with start after end — typically from swapping arguments, computing boundaries inclusively/exclusively incorrectly, or a clock-skewed timestamp source producing end in the past.

Common situations: Building a daily batch job with inclusive end-of-day timestamps equal to the next job's start; unit/integration tests using identical fixed timestamps for start and end; misconfiguring a reporting window where start/end come from env or config in different orders or timezones.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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