nautechsystems/nautilus_trader · error

invalid bar step: {e}

Error message

invalid bar step: {e}

What it means

`add_bar` converts the bar spec's step (a u32/nonzero) into an i32 for the Postgres `step` column. If the step exceeds `i32::MAX` (or the conversion otherwise fails) this error is returned. It guards against integer overflow when binding the parameter.

Source

Thrown at crates/infrastructure/src/sql/queries.rs:1350

        .map_err(|e| anyhow::anyhow!("Failed to load quotes: {e}"))
    }

    /// Inserts a `Bar` entry via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the SQL INSERT operation fails.
    pub async fn add_bar(pool: &PgPool, bar: &Bar) -> anyhow::Result<()> {
        if bar.bar_type.is_composite() {
            anyhow::bail!(
                "Cannot persist bar with composite bar type {}: the bar table stores only \
                 the standard form; standardize the bar type before persisting",
                bar.bar_type,
            );
        }

        let bar_step = i32::try_from(bar.bar_type.spec().step.get())
            .map_err(|e| anyhow::anyhow!("invalid bar step: {e}"))?;

        sqlx::query(r#"
            INSERT INTO "bar" (
                instrument_id, step, bar_aggregation, price_type, aggregation_source, open, high, low, close, volume, ts_event, ts_init, created_at, updated_at
            ) VALUES (
                $1, $2, $3::bar_aggregation, $4::price_type, $5::aggregation_source, $6, $7, $8, $9, $10, $11, $12, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
            )
            ON CONFLICT (id)
            DO UPDATE
            SET
                instrument_id = $1, step = $2, bar_aggregation = $3::bar_aggregation, price_type = $4::price_type, aggregation_source = $5::aggregation_source,
                open = $6, high = $7, low = $8, close = $9, volume = $10, ts_event = $11, ts_init = $12, updated_at = CURRENT_TIMESTAMP
        "#)
            .bind(bar.bar_type.instrument_id().to_string())
            .bind(bar_step)
            .bind(BarAggregationPg(bar.bar_type.spec().aggregation))
            .bind(PriceTypePg(bar.bar_type.spec().price_type))
            .bind(AggregationSourcePg(bar.bar_type.aggregation_source()))

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate the bar spec step fits in i32 (1..=2147483647) before constructing/persisting the bar
  2. Clamp or reject oversized steps at config load time
  3. Check the wrapped TryFromIntError to confirm it is an out-of-range step

Example fix

// before
sql_cache.add_bar(&pool, &bar).await?;
// after
assert!(bar.bar_type.spec().step.get() <= i32::MAX as u64, "bar step too large");
sql_cache.add_bar(&pool, &bar).await?;
Defensive patterns

Strategy: validation

Validate before calling

let step = bar.bar_type.spec().step.get();
if step == 0 || step > i32::MAX as u64 {
    return Err(anyhow::anyhow!("bar step {step} out of i32 range"));
}

Type guard

fn bar_step_fits(step: u64) -> bool { step >= 1 && step <= i32::MAX as u64 }

Try / catch

if let Err(e) = add_bar(&pool, &bar).await {
    if e.to_string().contains("invalid bar step") { /* reject or fix bar spec */ }
    return Err(e);
}

Prevention

When it happens

Trigger: Calling `add_bar(pool, &bar)` with a bar type whose `spec().step.get()` is larger than 2147483647 — practically only from programmatically constructed bar specs with absurd step values.

Common situations: Constructing BarAggregation specs from unvalidated config or external input where the step is an arbitrary u64/usize; buggy aggregation code producing huge steps.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/b06e714a23b9e417. Report an issue: GitHub.