nautechsystems/nautilus_trader · error
start must not be later than end
Error message
start must not be later than end
What it means
request_trade_ticks validates its time range up front: when both start and end timestamps are provided, start must not exceed end. An inverted range would produce an empty or nonsensical pagination window, so the call is rejected immediately.
Source
Thrown at crates/adapters/polymarket/src/http/data_api.rs:407
#[expect(clippy::too_many_arguments)]
pub async fn request_trade_ticks(
&self,
instrument_id: InstrumentId,
condition_id: &str,
token_id: &str,
price_precision: u8,
size_precision: u8,
start: Option<UnixNanos>,
end: Option<UnixNanos>,
limit: Option<u32>,
) -> anyhow::Result<Vec<TradeTick>> {
const PAGE_SIZE: u32 = 500;
const MAX_OFFSET: u32 = 10_000;
if let (Some(start), Some(end)) = (start, end)
&& start > end
{
anyhow::bail!("start must not be later than end");
}
if limit == Some(0) {
anyhow::bail!("limit must be greater than zero");
}
let start_secs = start.map(|value| (value.as_u64() / 1_000_000_000) as i64);
let end_secs = end.map(|value| (value.as_u64() / 1_000_000_000) as i64);
let protocol = OffsetProtocol::new(
"/trades",
PAGE_SIZE as usize,
trade_page_fingerprint,
Some((
MAX_OFFSET,
TradeTickStop::VenueOffsetCeiling(OffsetCeilingSource::Local),
)),
);
let reducer = TradeTickReducer {View on GitHub (pinned to 18893faf8b)
Solutions
- Swap or recompute the arguments so start <= end.
- Check timestamp units: the API takes Unix milliseconds (nanoseconds are converted internally); ensure both bounds use the same unit.
- When start and end are computed at different times, capture 'now' once and derive both bounds from it.
- Guard the call site with an assertion before invoking the API.
Example fix
// before let (start, end) = (end_ts, start_ts); // swapped api.request_trade_ticks(Some(cid), Some(start), Some(end), None).await?; // after assert!(start_ts <= end_ts); api.request_trade_ticks(Some(cid), Some(start_ts), Some(end_ts), None).await?;
Defensive patterns
Strategy: validation
Validate before calling
// Rust: validate the range before calling
fn valid_range(start: Option<u64>, end: Option<u64>) -> bool {
matches!((start, end), (None, _) | (_, None) | (Some(s), Some(e)) if s <= e)
}
assert!(valid_range(start_ts, end_ts)); Type guard
fn ordered_range(start: u64, end: u64) -> Option<(u64, u64)> {
(start <= end).then_some((start, end))
} Prevention
- Compute start and end from a single 'now' timestamp to avoid ordering drift.
- Use one consistent unit (Unix milliseconds) for all timestamps.
- Add a debug assertion on time ranges at every call site.
When it happens
Trigger: Calling request_trade_ticks with a start Unix-ms timestamp strictly greater than the end timestamp (e.g. (Some(start), Some(end)) where start > end).
Common situations: Swapped argument order at the call site; computing end from a 'now' variable captured before start in async code; unit mix-ups (seconds vs milliseconds) making start appear later; translating inclusive/exclusive bounds incorrectly across systems.
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
- limit must be greater than zero
- Polymarket Data API returned trade for condition {} while re
- Polymarket public trades API reached the historical offset c
- {e}
- Polymarket execution shutdown failed: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/971c8e017f7a90dc.
Report an issue: GitHub.