nautechsystems/nautilus_trader · error
Cannot persist bar with composite bar type {}: the bar table
Error message
Cannot persist bar with composite bar type {}: the bar table stores only the standard form; standardize the bar type before persisting What it means
Thrown by `add_bar` when persisting a `Bar` whose `BarType` is composite (e.g. with aggregation transforms or non-standard composition). The bars table only stores the standard bar form, so the caller must standardize the bar type before persisting; the library refuses to silently transform or lose composite information.
Source
Thrown at crates/infrastructure/src/sql/queries.rs:1342
) -> anyhow::Result<Vec<QuoteTick>> {
sqlx::query_as::<_, QuoteTickRow>(
r#"SELECT * FROM "quote" WHERE instrument_id = $1 ORDER BY ts_event ASC"#,
)
.bind(instrument_id.to_string())
.fetch_all(pool)
.await
.map(|rows| rows.into_iter().map(|row| row.0).collect())
.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
SETView on GitHub (pinned to 18893faf8b)
Solutions
- Call `bar.bar_type.standardize()` (build the bar from the standardized bar type) before persisting.
- Persist only bars with standard bar types; keep composite bars in memory or a separate store if needed.
- Filter composite bars out at the subscription/handler level before writing to the SQL cache.
Example fix
// before queries.add_bar(&pool, &bar).await?; // after let standard_bar = Bar::new(bar.bar_type.standardize(), bar.open, bar.high, bar.low, bar.close, bar.volume, bar.ts_event, bar.ts_init); queries.add_bar(&pool, &standard_bar).await?;
Defensive patterns
Strategy: validation
Validate before calling
if bar.bar_type.is_composite() {
// standardize before persisting
let standard_bar = Bar::new(
bar.bar_type.standardize(), bar.open, bar.high, bar.low,
bar.close, bar.volume, bar.ts_event, bar.ts_init,
);
} Type guard
fn is_standard_bar(bar: &Bar) -> bool { !bar.bar_type.is_composite() } Try / catch
match queries::add_bar(&pool, &bar).await {
Err(e) if e.to_string().contains("composite bar type") => {
let std_bar = standardize_bar(&bar);
queries::add_bar(&pool, &std_bar).await?;
}
other => other?,
} Prevention
- Always standardize bar types before any SQL cache write.
- Filter composite bars at the data-handler level.
- Keep composite aggregation outputs in a dedicated in-memory store, not the bar table.
When it happens
Trigger: Calling `BarQueries::add_bar(pool, &bar)` with a bar produced from a composite BarType (e.g. an aggregated/transformed bar such as renko/volume-derived bars, or a bar type with a non-standard composition) instead of a standard bar type.
Common situations: Persisting results of a composite aggregation pipeline directly; passing bars received from transformed subscriptions (e.g. `BarType::from_str("...-renko-...")`) straight into the cache write; forgetting `standardize()` when bridging live data into storage.
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
- Unsupported bar specification for AX: {step}-{:?}
- Derive only supports minute intervals 1, 5, 15, 30 (use HOUR
- Derive only supports hour intervals 1, 4, 8
- Derive only supports 1 DAY interval bars
- Invalid step in bar_type.spec.step: {step} for aggregation={
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7996aff7fda17e22.
Report an issue: GitHub.