nautechsystems/nautilus_trader · error
Invalid step: {step} (must be non-zero)
Error message
Invalid step: {step} (must be non-zero) What it means
BarAggregation's constructor `new_checked` requires the aggregation step to be non-zero. The step is stored as `NonZeroUsize` because a zero step would make bar aggregation meaningless (or cause division-by-zero logic downstream). Passing 0 is rejected immediately with this anyhow error.
Source
Thrown at crates/model/src/data/bar.rs:490
impl BarSpecification {
/// Creates a new [`BarSpecification`] instance with correctness checking.
///
/// # Errors
///
/// Returns an error 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.
///
/// # Notes
///
/// PyO3 requires a `Result` type for proper error handling and stacktrace printing in Python.
pub fn new_checked(
step: usize,
aggregation: BarAggregation,
price_type: PriceType,
) -> anyhow::Result<Self> {
let step = NonZeroUsize::new(step)
.ok_or(anyhow::anyhow!("Invalid step: {step} (must be non-zero)"))?;
Self::validate_step(step.get(), aggregation)?;
Ok(Self {
step,
aggregation,
price_type,
})
}
fn validate_step(step: usize, aggregation: BarAggregation) -> anyhow::Result<()> {
match aggregation {
BarAggregation::Millisecond => {
Self::validate_periodic_step(step, aggregation, 1000, false)?;
}
BarAggregation::Second | BarAggregation::Minute => {
Self::validate_periodic_step(step, aggregation, 60, false)?;
}
BarAggregation::Hour => Self::validate_periodic_step(step, aggregation, 24, false)?,View on GitHub (pinned to 18893faf8b)
Solutions
- Check the step value before constructing: reject or default any step == 0 in your config-loading code.
- If step comes from a config file, verify the field is present and set to >= 1 (e.g. `step = 100` for 100-tick bars).
- If step is computed, add an assertion/log when the computation yields 0 so the bug is caught at the source.
- Prefer constructing via `NonZeroUsize::new(step)` yourself at the call site to surface zero values early with your own context.
Example fix
// before let agg = BarAggregation::new_checked(cfg.step, BarAggregation::Tick, PriceType::Last)?; // after let step = cfg.step.max(1); let agg = BarAggregation::new_checked(step, BarAggregation::Tick, PriceType::Last)?;
Defensive patterns
Strategy: validation
Validate before calling
fn valid_step(step: usize) -> bool { step != 0 }
if !valid_step(cfg.step) { return Err(anyhow!("step must be non-zero, got {}", cfg.step)); } Prevention
- Store step as NonZeroUsize in your own config types so zero is unrepresentable.
- Validate bar aggregation config at startup before any data flow begins.
- Use derive defaults like #[serde(default = "default_step")] returning 1 instead of 0.
When it happens
Trigger: Calling `BarAggregation::new_checked(0, aggregation, price_type)` — or building one from a config value that defaulted to or was parsed as 0 — triggers the error before `validate_step` even runs.
Common situations: Config files where a step field was omitted and defaulted to 0; deserializing a strategy config where step was typed as a plain usize with a Default derive; a calculation producing a 0 step (e.g. multiplying by 0 or an empty tick count).
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/660bac8d9dee39b2.
Report an issue: GitHub.