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
- Swap or correct the start/end datetimes on the request so start <= end before calling execute_request
- Log both datetimes at the call site and verify the normalization (UTC, correct units) applied to them
- If the request is intentionally empty/degenerate, skip dispatching rather than sending an inverted range
- 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
- Always construct date-range requests with (start, end) in that argument order
- Normalize all datetimes to UTC unix nanoseconds before comparing
- Unit-test request builders with known inverted-range inputs
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.