{"record":{"id":"9aceef42c0ffccca","repo":"nautechsystems/nautilus_trader","slug":"start-s-must-be-before-end-e","errorCode":null,"errorMessage":"start ({s}) must be before end ({e})","messagePattern":"start \\((.+?)\\) must be before end \\((.+?)\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/dydx/src/http/client.rs","lineNumber":1222,"sourceCode":"    /// Returns an error if the HTTP request fails, response cannot be parsed,\n    /// or the instrument is not found in the cache.\n    ///\n    /// # Panics\n    ///\n    /// This function will panic if the API returns a non-empty trades response\n    /// but `last()` on the trades vector returns `None` (should never happen).\n    pub async fn request_trade_ticks(\n        &self,\n        instrument_id: InstrumentId,\n        start: Option<Timestamp>,\n        end: Option<Timestamp>,\n        limit: Option<u32>,\n    ) -> anyhow::Result<Vec<TradeTick>> {\n        const DYDX_MAX_TRADES_PER_REQUEST: u32 = 1_000;\n\n        // Validation\n        if let (Some(s), Some(e)) = (start, end) {\n            anyhow::ensure!(s < e, \"start ({s}) must be before end ({e})\");\n        }\n\n        let instrument = self\n            .get_instrument(&instrument_id)\n            .ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?;\n\n        let ticker = extract_raw_symbol(instrument_id.symbol.as_str());\n        let price_precision = instrument.price_precision();\n        let size_precision = instrument.size_precision();\n        let ts_init = self.generate_ts_init();\n\n        // We always start pagination from the chain head (cursor = None). An earlier\n        // version used `DEFAULT_BLOCK_TIME_SECS` with `get_height()` to skip directly\n        // to an estimated target block, but any hardcoded block-time estimate that\n        // underestimates the true average lands the cursor BEFORE the real `end`\n        // block and silently drops the trades in the skipped window. Walking back\n        // from head costs a few extra round-trips for stale `end` times but is\n        // always correct. Per-call trades above `end` are filtered inside the loop.","sourceCodeStart":1204,"sourceCodeEnd":1240,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/dydx/src/http/client.rs#L1204-L1240","documentation":"request_trade_ticks validates that when both start and end UnixNanos bounds are supplied, start must be strictly less than end. anyhow::ensure! aborts the request with this message when start >= end, since dYdX would return an error or empty result for a reversed/empty interval.","triggerScenarios":"Calling request_trade_ticks(instrument_id, Some(start), Some(end), ...) with start == end or start > end, e.g. when end is computed as \"last processed timestamp\" and start equals it on the first poll.","commonSituations":"Incremental polling logic where the new start equals the previous end; clock skew making timestamps inverted; passing the same timestamp for both bounds expecting an inclusive single-instant query.","solutions":["Before calling, check start < end and skip/adjust the request when the range is empty","Make the end bound exclusive in your caller logic (e.g. last_ts + 1)","Only pass bounds when both are present and ordered; otherwise pass None"],"exampleFix":"// before\nclient.request_trade_ticks(instrument_id, Some(start), Some(last_ts), None).await?;\n// after\nif start < last_ts {\n    client.request_trade_ticks(instrument_id, Some(start), Some(last_ts), None).await?;\n} // else: empty range, nothing to fetch","handlingStrategy":"validation","validationCode":"if let (Some(s), Some(e)) = (start, end) {\n    anyhow::ensure!(s < e, \"trade tick range empty: start={s} end={e}\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check range emptiness before each poll and skip no-op requests","Treat end as exclusive in your watermark logic","Unit-test incremental fetch boundaries (start == end, start > end)"],"tags":["dydx","validation","time-range","http-client"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}