nautechsystems/nautilus_trader · error · anyhow::Error
snapshot interval must be positive
Error message
snapshot interval must be positive
What it means
When converting a Tardis options-chain CSV with thinning enabled, `snapshot_interval` controls the bucketing of rows into snapshots. The interval is converted to microseconds and must be greater than zero; a zero (or negative, unrepresentable) interval would make bucketing degenerate (division by zero) so the library rejects it with `anyhow::ensure!`. Note a separate error covers intervals too large for u64 microseconds.
Source
Thrown at crates/adapters/tardis/src/csv/convert.rs:125
let record: TardisOptionsChainRecord = csv_record
.deserialize(None)
.with_context(|| format!("failed to parse CSV file {}", filepath.display()))?;
let instrument_id = parse_instrument_id(&record.exchange, record.symbol);
precision_by_instrument
.entry(instrument_id)
.or_insert_with(|| {
OptionsChainPrecision::new(config.price_precision, config.size_precision)
})
.update(&record, config.price_precision, config.size_precision);
instrument_states
.entry(instrument_id)
.and_modify(|state| state.update_activation(record.local_timestamp))
.or_insert_with(|| InstrumentBuildState::new(record.clone()));
if let Some(interval) = config.snapshot_interval {
let interval_us = u64::try_from(interval.as_micros())
.context("snapshot interval exceeds u64 microseconds")?;
anyhow::ensure!(interval_us > 0, "snapshot interval must be positive");
let bucket = record.local_timestamp / interval_us;
if let Some(current_bucket) = current_bucket {
anyhow::ensure!(
bucket >= current_bucket,
"options_chain CSV rows must be ordered by local_timestamp when thinning"
);
}
if current_bucket.is_none_or(|current| bucket > current) {
flush_pending_records_before(
&catalog,
&mut pending_records,
&mut data_buffers,
&precision_by_instrument,
bucket,
config.extract_bbo_as_quotes,
)?;View on GitHub (pinned to 18893faf8b)
Solutions
- Set `snapshot_interval` to a positive duration with at least 1 microsecond, e.g. `Some(Duration::from_secs(1))`.
- Validate/parse user input so zero or sub-microsecond values are rejected before calling the converter.
- If no thinning is desired, pass `snapshot_interval: None` instead of Some(zero).
Example fix
// before
let config = OptionsChainCsvConfig { snapshot_interval: Some(Duration::from_secs(0)), .. };
// after
let config = OptionsChainCsvConfig { snapshot_interval: Some(Duration::from_secs(1)), .. }; Defensive patterns
Strategy: validation
Validate before calling
from datetime import timedelta, timedelta as td
def validate_snapshot_interval(interval: timedelta | None) -> None:
if interval is not None and interval <= timedelta(0):
raise ValueError(f"snapshot_interval must be positive, got {interval}") Type guard
def is_valid_interval(interval: timedelta | None) -> bool:
return interval is None or interval > timedelta(0) Prevention
- Never default snapshot_interval to zero; use None when thinning is unwanted
- Parse durations with an explicit unit and reject sub-microsecond values
- Add a config-validation step that checks positivity before conversion
When it happens
Trigger: Calling `convert_options_chain_csv` (or `py_convert_tardis_options_chain_csv`) with `config.snapshot_interval = Some(Duration::ZERO)` (or a duration that rounds to 0 microseconds, e.g. Duration::from_nanos(1)).
Common situations: Reading the interval from config/CLI where a default of 0 was left in place; parsing a user-supplied value like "0s" or "0ms"; constructing the duration with the wrong unit so it truncates to zero microseconds.
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
- Verified action requires the configured provider identities
- options_chain CSV rows must be ordered by local_timestamp wh
- Invalid interval_ns: {interval_ns} (must be non-zero)
- Connection rate keys require a connection rate limiter
- Connection rate limiter requires at least one connection rat
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e3d74fc94ce76ca7.
Report an issue: GitHub.