nautechsystems/nautilus_trader · error

Aggregation type {} not supported for time bars

Error message

Aggregation type {} not supported for time bars

What it means

`get_time_bar_start` computes the aligned start timestamp for time-based bars. Its match handles the eight time aggregations; the `_ => panic!("Aggregation type {} not supported for time bars", spec.aggregation)` arm fires when the bar's aggregation is inherently non-time based, so no calendar-aligned start exists. This is called from timer setup (`start_timer_internal`) when registering time-bar aggregators.

Source

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

                year = year
                    .checked_sub(step_i32)
                    .expect("year arithmetic underflow");
            }

            loop {
                let next_year = year
                    .checked_add(step_i32)
                    .expect("year arithmetic overflow");

                if year_start(next_year) > now {
                    break;
                }
                year = next_year;
            }

            year_start(year)
        }
        _ => panic!(
            "Aggregation type {} not supported for time bars",
            spec.aggregation
        ),
    }
}

/// Finds the closest smaller time based on a daily time origin and period.
///
/// This function calculates the most recent time that is aligned with the given period
/// and is less than or equal to the current time.
fn find_closest_smaller_time(
    now: Timestamp,
    daily_time_origin: SignedDuration,
    period: SignedDuration,
) -> Timestamp {
    // Floor to start of day
    let day_start = Offset::UTC
        .to_timestamp(Offset::UTC.to_datetime(now).date().at(0, 0, 0, 0))

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the BarType/aggregation is time based (use `aggregation.is_time_based()`-style check or match on the enum) before computing a time bar start.
  2. Route tick/value bar types to their non-timer aggregation path instead of `start_timer_internal`.
  3. Fix the BarType string in config so the aggregation is a time-based variant.

Example fix

// before
let start = get_time_bar_start(now, &bar_type, origin); // panics for VALUE bars

// after
if !matches!(bar_type.spec().aggregation,
    BarAggregation::Millisecond | BarAggregation::Second | BarAggregation::Minute
    | BarAggregation::Hour | BarAggregation::Day | BarAggregation::Week
    | BarAggregation::Month | BarAggregation::Year) {
    anyhow::bail!("cannot compute time bar start for non-time aggregation");
}
let start = get_time_bar_start(now, &bar_type, origin);
Defensive patterns

Strategy: validation

Validate before calling

# Python
TIME_AGGS = {BarAggregation.MILLISECOND, BarAggregation.SECOND, BarAggregation.MINUTE,
             BarAggregation.HOUR, BarAggregation.DAY, BarAggregation.WEEK,
             BarAggregation.MONTH, BarAggregation.YEAR}
assert bar_type.spec().aggregation in TIME_AGGS, "time bar start requires a time-based aggregation"

Type guard

def is_time_based(a) -> bool:
    from nautilus_trader.model.enums import BarAggregation
    return a in {BarAggregation.MILLISECOND, BarAggregation.SECOND, BarAggregation.MINUTE,
                 BarAggregation.HOUR, BarAggregation.DAY, BarAggregation.WEEK,
                 BarAggregation.MONTH, BarAggregation.YEAR}

Prevention

When it happens

Trigger: Registering or starting a bar aggregator whose BarSpecification uses a non-time aggregation (Tick, Value) but a time-based start computation; misconfigured BarType strings like `EUR/USD.LAST-TICK` routed through the time-bar start path.

Common situations: Strategy configs mixing tick bars and time bars where the timer registration path doesn't dispatch on aggregation kind; custom adapter code calling `get_time_bar_start` for a bar type parsed from user config.

Related errors


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