nautechsystems/nautilus_trader · error · anyhow::Error

Start date must be before end date

Error message

Start date must be before end date

What it means

request_bars validates that the optional start_date_time is strictly earlier than end_date_time. An equal or later start would produce an empty or inverted IB history window, so the call is rejected up front.

Source

Thrown at crates/adapters/interactive_brokers/src/historical/client.rs:243

        &self,
        bar_specifications: Vec<&str>,
        end_date_time: Timestamp,
        start_date_time: Option<Timestamp>,
        duration: Option<&str>,
        contracts: Option<Vec<Contract>>,
        instrument_ids: Option<Vec<InstrumentId>>,
        use_rth: bool,
        timeout: u64,
    ) -> anyhow::Result<Vec<Bar>> {
        // Validate inputs
        if start_date_time.is_some() && duration.is_some() {
            anyhow::bail!("Either start_date_time or duration should be provided, not both");
        }

        if let Some(start) = start_date_time
            && start >= end_date_time
        {
            anyhow::bail!("Start date must be before end date");
        }

        if let Some(duration) = duration {
            duration.parse::<historical::Duration>().with_context(|| {
                format!("duration must be in format: 'int S|D|W|M|Y', was '{duration}'")
            })?;
        }

        let contracts = contracts.unwrap_or_default();
        let instrument_ids = instrument_ids.unwrap_or_default();

        if contracts.is_empty() && instrument_ids.is_empty() {
            anyhow::bail!("Either contracts or instrument_ids must be provided");
        }

        // Convert instrument IDs to contracts using instrument provider
        let mut all_contracts = contracts;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure start_date_time < end_date_time before calling; swap them if inverted.
  2. Truncate or floor start below the end boundary if they collide at a day edge.
  3. Drop start_date_time and pass a duration string instead if the exact window is flexible.

Example fix

// before
client.request_bars(&["1-DAY-LAST"], end_ts, Some(end_ts), None, None, Some(ids), true, 60).await?;
// after
let start = end_ts - jiff::SignedDuration::from_hours(24);
client.request_bars(&["1-DAY-LAST"], end_ts, Some(start), None, None, Some(ids), true, 60).await?;
Defensive patterns

Strategy: validation

Validate before calling

debug_assert!(start < end, "start must preced end");
if start >= end { return Err("invalid bar window: start >= end"); }

Prevention

When it happens

Trigger: Calling request_bars with start_date_time >= end_date_time (e.g. both set to the same day boundary, or start/end swapped).

Common situations: Computing start/end from UTC timestamps with timezone mix-ups; accidental argument swap; using 'now' for both fields; off-by-one when flooring end to the day while start carries a time-of-day.

Related errors


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