nautechsystems/nautilus_trader · error
Data has been added but not sorted, call `engine.sort_data()
Error message
Data has been added but not sorted, call `engine.sort_data()` or use `engine.add_data(..., sort=true)` before running
What it means
The backtest engine requires its internal data to be chronologically sorted before run_impl executes. If data was added without sorting (the engine's `sorted` flag is false), running is refused so the time-driven simulation does not process out-of-order events.
Source
Thrown at crates/backtest/src/engine.rs:730
// and flush callbacks that execute after the main data loop) so the
// trader and engines actually stop.
// Streaming batches retain commands deferred by other instruments,
// and end() performs the unrestricted drain after all batches are loaded.
if !streaming || self.force_stop || self.kernel.is_shutdown_requested() {
self.end()?;
}
Ok(())
}
fn run_impl(
&mut self,
start: Option<UnixNanos>,
end: Option<UnixNanos>,
run_config_id: Option<String>,
streaming: bool,
) -> anyhow::Result<()> {
anyhow::ensure!(
self.sorted,
"Data has been added but not sorted, call `engine.sort_data()` or use \
`engine.add_data(..., sort=true)` before running"
);
for exchange in self.venues.values() {
let exchange = exchange.borrow();
let book_type_has_depth = exchange.book_type() as u8 > BookType::L1_MBP as u8;
if !book_type_has_depth {
continue;
}
for instrument_id in exchange.instrument_ids() {
let has_data = self.has_data.contains(instrument_id);
let missing_book_data = !self.has_book_data.contains(instrument_id)
&& !self.has_book_processed.contains(instrument_id);
if has_data && missing_book_data {View on GitHub (pinned to 18893faf8b)
Solutions
- Call engine.sort_data() before run().
- Pass sort=true to the last add_data/add_data_batch call.
- Ensure your data-loading path always ends with a sort; consider sorting once after all batches for efficiency.
Example fix
// before engine.add_data_batch(data, false); engine.run(); // after engine.add_data_batch(data, false); engine.sort_data(); engine.run();
Defensive patterns
Strategy: validation
Validate before calling
if !engine_is_sorted() {
engine.sort_data(); // or re-add with sort=true
}
engine.run(None, None, None, false); Try / catch
match engine.run(None, None, None, false) {
Err(e) if e.to_string().contains("not sorted") => {
engine.sort_data();
engine.run(None, None, None, false)?;
}
r => r?,
} Prevention
- Make the final data-loading step always call sort_data().
- Prefer add_data(..., sort=true) unless you deliberately batch-sort.
- Add an assertion in your loader that the engine is sorted before run.
When it happens
Trigger: Calling engine.run(...) after add_data/add_data_batch with sort=false (or raw additions without engine.sort_data()); the flag stays false because sort was never invoked.
Common situations: Adding data in multiple batches with sort=false for performance and forgetting the final sort_data(); constructing the engine via a custom loader that bypasses the sorted flag; interleaving add_data and add_data_batch calls where the last one had sort=false.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Instrument {first_instrument_id} for the given data not foun
- bar_type.aggregation_source must be External, was {:?}
- start was > end
- Cannot handle command: {command:?}
- Latency model should be initialized
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/40dcd3d499de13e9.
Report an issue: GitHub.