nautechsystems/nautilus_trader · error · EventStoreError::Backend

catalog marker join load failed for {data_cls}: {error}

Error message

catalog marker join load failed for {data_cls}: {error}

What it means

After a slice plan succeeds, `load_replayable_records` asks the catalog to load the planned slice (`load_slice`). Any backend failure during loading is converted into `EventStoreError::Backend` with 'catalog marker join load failed for {data_cls}: {error}', preserving the data class and underlying error for diagnosis.

Source

Thrown at crates/event_store/src/markers/join.rs:180

) -> Result<CatalogSliceCoverage, EventStoreError>
where
    C: ReplayCatalog + ?Sized,
{
    catalog
        .plan_slice(query)
        .map_err(|e| catalog_error(&query.data_cls, "plan", e))
}

fn load_slice<C>(
    catalog: &mut C,
    plan: &CatalogSlicePlan,
) -> Result<Vec<CatalogReplayRecord>, EventStoreError>
where
    C: ReplayCatalog + ?Sized,
{
    catalog
        .load_slice(plan)
        .map_err(|e| catalog_error(&plan.query.data_cls, "load", e))
}

fn catalog_error(data_cls: &str, action: &str, error: impl Display) -> EventStoreError {
    EventStoreError::Backend(format!(
        "catalog marker join {action} failed for {data_cls}: {error}"
    ))
}

#[cfg(test)]
mod tests {
    use nautilus_core::UnixNanos;
    use nautilus_model::{
        data::{Bar, BarType, QuoteTick, TradeTick},
        enums::AggressorSide,
        identifiers::{InstrumentId, TradeId},
        types::{Price, Quantity},
    };
    use rstest::rstest;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the wrapped `{error}` to find the underlying load failure (I/O, missing files, connectivity)
  2. Verify the planned data files/ranges exist and are readable at the catalog backend
  3. Retry the replay if the failure was transient (connection loss, temporary unavailability)
  4. Fix `load_slice` if this comes from a custom `ReplayCatalog` implementation

Example fix

// before: load errors abort the replay unguarded
let records = store.load_replayable_records(&query)?;
// after: retry transient backend load failures
let records = match store.load_replayable_records(&query) {
    Ok(r) => r,
    Err(e) if e.is_transient() => {
        backoff::retry(|| store.load_replayable_records(&query))?
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Plan first and check loadability is implied; at minimum pre-validate the range
let plan = catalog.plan_slice(&query).expect("plan");
// optionally probe a small slice first
let probe = catalog.load_slice(&plan.limit_to(1))?;

Try / catch

match store.load_replayable_records(&query) {
    Err(EventStoreError::Backend(msg)) if msg.contains("load failed") => {
        eprintln!("transient backend load failure, retrying: {msg}");
        // retry with backoff
    }
    Err(e) => return Err(e.into()),
    Ok(records) => process(records),
}

Prevention

When it happens

Trigger: Calling `load_replayable_records` where planning succeeds but loading fails — storage I/O errors, missing/corrupt data files for the planned range, backend connection loss mid-load, or a custom `ReplayCatalog` whose `load_slice` returns an error.

Common situations: Replaying from a data catalog whose files were moved/deleted or are partially written; network interruptions to object-storage-backed catalogs; concurrent modification of catalog data during replay.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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