nautechsystems/nautilus_trader · error · anyhow::Error
Invalid bar spec: empty string
Error message
Invalid bar spec: empty string
What it means
Tardis parse_bar_spec splits a bar spec string on '_' and reads the last part for the numeric step plus aggregation suffix. If parts.last() returns None — which for str::split can only happen via an empty input being mishandled downstream — the function rejects the spec as empty. It is the first guard against malformed bar specification strings.
Source
Thrown at crates/adapters/tardis/src/common/parse.rs:328
} else if is_snapshot {
BookAction::Add
} else {
BookAction::Update
}
}
/// Parses a Nautilus bar specification from the given Tardis string `value`.
///
/// The [`PriceType`] is always `LAST` for Tardis trade bars.
///
/// # Errors
///
/// Returns an error if the specification format is invalid or if the aggregation suffix is unsupported.
pub fn parse_bar_spec(value: &str) -> anyhow::Result<BarSpecification> {
let parts: Vec<&str> = value.split('_').collect();
let last_part = parts
.last()
.ok_or_else(|| anyhow::anyhow!("Invalid bar spec: empty string"))?;
let split_idx = last_part
.chars()
.position(|c| !c.is_ascii_digit())
.ok_or_else(|| anyhow::anyhow!("Invalid bar spec: no aggregation suffix in '{value}'"))?;
let (step_str, suffix) = last_part.split_at(split_idx);
let step: usize = step_str
.parse()
.map_err(|e| anyhow::anyhow!("Invalid step in bar spec '{value}': {e}"))?;
let aggregation = match suffix {
"ms" => BarAggregation::Millisecond,
"s" => BarAggregation::Second,
"m" => BarAggregation::Minute,
"ticks" => BarAggregation::Tick,
"vol" => BarAggregation::Volume,
_ => anyhow::bail!("Unsupported bar aggregation type: '{suffix}'"),
};View on GitHub (pinned to 18893faf8b)
Solutions
- Check where the bar spec string originates — an empty value usually means a missing config field or a bad split of a comma-separated list
- Provide a valid bar spec like "1-MINUTE" / "100-TICK" (step + suffix) in the subscription config
- Filter out empty strings before calling parse_bar_spec
- Validate bar specs at config-load time so the error surfaces early
Example fix
// before: empty entries slip through
for spec in csv_string.split(',') {
let bar = parse_bar_spec(spec)?;
}
// after: skip empties
for spec in csv_string.split(',').map(str::trim).filter(|s| !s.is_empty()) {
let bar = parse_bar_spec(spec)?;
} Defensive patterns
Strategy: validation
Validate before calling
fn valid_bar_spec_input(value: &str) -> bool { !value.trim().is_empty() } Try / catch
match parse_bar_spec(spec) {
Ok(bar) => /* proceed */,
Err(e) if e.to_string().contains("empty string") => {
log::warn!("blank bar spec in config, using default '1m'");
parse_bar_spec("1m")?
}
Err(e) => return Err(e.into()),
} Prevention
- Filter empty entries when splitting CSV-style spec lists
- Validate bar specs at config-load time
- Always provide an explicit bar spec for Tardis subscriptions
When it happens
Trigger: Calling parse_bar_spec("") or a whitespace-only string; passing a None-derived/empty value from upstream config or a Tardis bar message with a missing spec field.
Common situations: Empty bar_spec entry in Tardis subscription config (e.g. trailing comma in a CSV list split into parts); bar spec field absent in a replayed instrument definition.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Invalid bar spec: no aggregation suffix in '{value}'
- Invalid step in bar spec '{value}': {e}
- unrecognized side '{side}'
- quantity size={} cannot be represented with precision={}
- Gamma {scope} filter '{key}' must be true or false, was '{va
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e19b9298ecb277c8.
Report an issue: GitHub.