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.
///
/// # PanicsView on GitHub (pinned to 18893faf8b)
Solutions
- Choose a step that evenly divides the subunits (e.g. 1, 2, 5, 10, 15, 30 for minutes)
- Let validate_step compute a valid step instead of hardcoding one
- 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
- Prefer canonical steps (1, 2, 5, 10, 15, 30) within each unit
- Validate bar specs at config load time
- Compute steps from durations rather than hardcoding arbitrary values
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
- Invalid step in bar_type.spec.step: {step} for aggregation={
- Derive only supports minute intervals 1, 5, 15, 30 (use HOUR
- Derive only supports hour intervals 1, 4, 8
- Derive only supports 1 DAY interval bars
- Only EXTERNAL aggregation is supported
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/88812eed79a2699c.
Report an issue: GitHub.