nautechsystems/nautilus_trader · error

Invalid step in bar_type.spec.step: {step} for aggregation={

Error message

Invalid step in bar_type.spec.step: {step} for aggregation={aggregation}. step must evenly divide {subunits} (so it is periodic).

What it means

validate_periodic_step requires the step to evenly divide the unit's subunits (e.g. seconds in a minute) so bars occur at fixed periodic offsets within the unit. A step that does not divide evenly would produce irregular boundaries and is rejected.

Source

Thrown at crates/model/src/data/bar.rs:526

            BarAggregation::Hour => Self::validate_periodic_step(step, aggregation, 24, false)?,
            // 12-MONTH is allowed (unlike other full-subunit steps) because the shipped
            // BAR_SPEC_12_MONTH_LAST constant and OKX yearly candles depend on it
            BarAggregation::Month => Self::validate_periodic_step(step, aggregation, 12, true)?,
            BarAggregation::Day | BarAggregation::Week | BarAggregation::Year => {}
            _ => return Ok(()),
        }

        try_time_interval(step, aggregation).map(|_| ())
    }

    fn validate_periodic_step(
        step: usize,
        aggregation: BarAggregation,
        subunits: usize,
        allow_equal: bool,
    ) -> anyhow::Result<()> {
        if !subunits.is_multiple_of(step) {
            anyhow::bail!(
                "Invalid step in bar_type.spec.step: {step} for aggregation={aggregation}. \
                 step must evenly divide {subunits} (so it is periodic).",
            );
        }

        if !allow_equal && subunits == step {
            anyhow::bail!(
                "Invalid step in bar_type.spec.step: {step} for aggregation={aggregation}. \
                 step must not be {subunits}. Use higher aggregation unit instead.",
            );
        }

        Ok(())
    }

    /// Creates a new [`BarSpecification`] instance.
    ///
    /// # Panics

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Choose a step that evenly divides the subunits (e.g. 1, 2, 5, 10, 15, 30 for minutes)
  2. Let validate_step compute a valid step instead of hardcoding one
  3. Reject/repair the bar spec in config validation before creating the BarType

Example fix

// before: 7-second step in 60-second minute (not periodic)
let step = 7;
// after
let step = 10; // divides 60 evenly
bar_type_spec.validate_step()?;
Defensive patterns

Strategy: validation

Validate before calling

if subunits % step != 0 {
    return Err(anyhow!("step {step} must evenly divide {subunits}"));
}
validate_periodic_step(step, aggregation, subunits, allow_equal)?;

Try / catch

if let Err(e) = validate_periodic_step(step, aggregation, subunits, allow_equal) {
    // fall back to a standard step (e.g. largest divisor <= requested)
}

Prevention

When it happens

Trigger: Constructing/validating a BarTypeSpec whose step (e.g. 7 seconds in a 60-subunit minute) does not evenly divide the parent unit's subunit count.

Common situations: Config mistakes like step=45 for second-based bars within a minute when the divisor check fails, hand-written bar specs, or programmatically generated steps from arbitrary durations.

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/88812eed79a2699c. Report an issue: GitHub.