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 not be {subunits}. Use higher aggregation unit instead.

What it means

When allow_equal is false, validate_periodic_step rejects a step exactly equal to the unit's subunit count, because such a bar is better expressed with the next higher aggregation unit (e.g. a 60-second step should be a 1-minute bar).

Source

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

        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
    ///
    /// Panics if `step` is not positive (> 0), if `step` is not valid for
    /// a fixed-subunit time aggregation, or if a time-aggregated `step`
    /// overflows the representable duration or nanosecond interval.
    #[must_use]
    pub fn new(step: usize, aggregation: BarAggregation, price_type: PriceType) -> Self {
        Self::new_checked(step, aggregation, price_type).expect(FAILED)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Replace the step with the next aggregation unit (60s -> 1 Minute step 1)
  2. If equality is truly intended, validate with allow_equal=true
  3. Normalize step/aggregation pairs in config loading

Example fix

// before
BarTypeSpec::new(Second, 60)?; // bails: equals subunits
// after
BarTypeSpec::new(Minute, 1)?;
Defensive patterns

Strategy: validation

Validate before calling

if step == subunits && !allow_equal {
    // promote to next aggregation unit: e.g. Second(60) -> Minute(1)
}
validate_periodic_step(step, aggregation, subunits, false)?;

Try / catch

match validate_periodic_step(step, aggregation, subunits, false) {
    Err(e) if e.to_string().contains("Use higher aggregation unit") => { /* rebuild spec with higher unit */ }
    other => other?,
}

Prevention

When it happens

Trigger: Specifying a step equal to the parent unit (60-second step in Second aggregation, 60-minute step in Minute aggregation) while allow_equal=false.

Common situations: Users writing bar specs in the lower unit out of habit ("60s" instead of "1m"); code generators that normalize durations downward; config migrations.

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