nautechsystems/nautilus_trader · error

Cannot dispatch request, start {start_ns} was greater than e

Error message

Cannot dispatch request, start {start_ns} was greater than end {end_ns}

What it means

The data engine's dispatch_date_range_request bounds each date-range request into (start_ns, end_ns) unix-nanoseconds and refuses to dispatch when the start is after the end. This guard prevents catalog/client fan-out from iterating an inverted, empty window. It fires after bound_request_dates normalization, so the supplied request dates themselves (possibly clamped) are inverted.

Source

Thrown at crates/data/src/engine/streaming.rs:232

        };

        let now_ns = self.clock.borrow().timestamp_ns();
        let now_dt = now_ns.to_datetime_utc();
        let query_past_data = request_params(&req)
            .and_then(|p| p.get(PARAM_SUBSCRIPTION_NAME))
            .is_none();

        let (start_dt, end_dt) = bound_request_dates(
            request_start(&req),
            request_end(&req),
            now_dt,
            query_past_data,
        );
        let start_ns = datetime_to_unix_nanos_or_zero(start_dt);
        let end_ns = datetime_to_unix_nanos_or_zero(end_dt);

        if start_ns > end_ns {
            anyhow::bail!(
                "Cannot dispatch request, start {start_ns} was greater than end {end_ns}"
            );
        }

        let client_id = req.client_id().copied();
        let venue = req.venue().copied();
        let used_client_id = self
            .get_client(client_id.as_ref(), venue.as_ref())
            .map(|client| client.client_id());

        // Floor the catalog window to the UTC day boundary so the day-start F_SNAPSHOT frame is
        // selected and read for the snapshot replay; client gaps keep the original window.
        // The parent request keeps its original start, so the merged response trims back to it.
        let (catalog_start_dt, catalog_start_ns) = if matches!(req, RequestCommand::BookDeltas(_))
            && request_params(&req)
                .and_then(|p| p.get_bool(PARAM_FROM_DAY_START))
                .unwrap_or(true)
        {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Swap or correct the start/end datetimes on the request so start <= end before calling execute_request
  2. Log both datetimes at the call site and verify the normalization (UTC, correct units) applied to them
  3. If the request is intentionally empty/degenerate, skip dispatching rather than sending an inverted range
  4. Validate bounds client-side before issuing any date-range catalog request

Example fix

// before
engine.execute_request(cmd_with(start: "2024-06-01", end: "2024-05-01"))
// after
let (start, end) = ("2024-05-01", "2024-06-01");
assert!(start <= end);
engine.execute_request(cmd_with(start, end));
Defensive patterns

Strategy: validation

Validate before calling

fn validate_range(start_ns: u64, end_ns: u64) -> Result<(), String> {
    if start_ns > end_ns { Err(format!("start {start_ns} > end {end_ns}")) } else { Ok(()) }
}

Prevention

When it happens

Trigger: Calling execute_request with a RequestCommand carrying a start datetime strictly later than its end datetime (e.g. start=2024-06-01, end=2024-05-01), such that after bounding against 'now' the start_ns > end_ns.

Common situations: Swapping start/end arguments when building a historical data request; passing a start in the future relative to a clamped end; timezone mishandling that shifts one bound across the other; unit mixups (seconds vs ms) that make the start parse far later than the end.

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/6b35cbcb918ce87b. Report an issue: GitHub.