nautechsystems/nautilus_trader · error

Continuous future transition times must be strictly increasi

Error message

Continuous future transition times must be strictly increasing, was {value}

What it means

Transition times in the continuous-future `transitions` table must be strictly increasing. A row whose transition_time_ns is equal to or earlier than the previous row's makes the roll schedule ambiguous and is rejected.

Source

Thrown at crates/data/src/engine/requests.rs:543

    let mut transitions = Vec::with_capacity(values.len());
    let mut previous_transition_time_ns = None;
    let mut previous_post_instrument_id = None;

    for row_value in values {
        let row = row_value.as_object().with_context(|| {
            format!("Continuous future transition must be an object, was {row_value}")
        })?;
        let transition_time_ns = row
            .get("transition_time_ns")
            .and_then(Value::as_u64)
            .with_context(|| {
                format!("Invalid continuous future transition_time_ns, was {row_value}")
            })?;

        if let Some(previous) = previous_transition_time_ns
            && transition_time_ns <= previous
        {
            anyhow::bail!(
                "Continuous future transition times must be strictly increasing, was {value}"
            );
        }
        previous_transition_time_ns = Some(transition_time_ns);

        let pre_instrument_id =
            parse_transition_instrument_id(row.get("pre_instrument_id"), target_bar_type)?;
        let post_instrument_id =
            parse_transition_instrument_id(row.get("post_instrument_id"), target_bar_type)?;
        if pre_instrument_id.venue != target_venue || post_instrument_id.venue != target_venue {
            anyhow::bail!(
                "Continuous future segment venue mismatch for {target_bar_type}: target venue {target_venue}, segment venues pre={}, post={}",
                pre_instrument_id.venue,
                post_instrument_id.venue,
            );
        }

        if let Some(previous) = previous_post_instrument_id

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Sort transitions by transition_time_ns ascending and de-duplicate identical timestamps before sending.
  2. Ensure all timestamps use the same unit (nanoseconds since epoch).
  3. Fix the data generator/provider so each roll has a unique, increasing transition time.

Example fix

// before
transitions.sort_by_key(|t| t.contract_symbol);
// after
transitions.sort_by_key(|t| t.transition_time_ns);
transitions.dedup_by_key(|t| t.transition_time_ns);
Defensive patterns

Strategy: validation

Validate before calling

for w in transitions.windows(2) {
    assert!(w[1].transition_time_ns > w[0].transition_time_ns, "transition times must strictly increase");
}

Prevention

When it happens

Trigger: parse_transitions encounters a row where transition_time_ns <= the previous row's transition_time_ns — duplicated timestamps or out-of-order rows in the `transitions` request parameter.

Common situations: Sorting transitions by string contract ID instead of by time; duplicated roll records from concatenating overlapping data pulls; timestamp units mixed (seconds vs nanoseconds) making later rows appear earlier.

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/3705ba9ad219e755. Report an issue: GitHub.