nautechsystems/nautilus_trader · error
Python SimulationModule.process failed: {e}
Error message
Python SimulationModule.process failed: {e} What it means
FFI boundary error in the Python SimulationModule wrapper: the module's Python process() callback raised an exception or returned a bad value; the Python error is embedded in the message and the simulation step fails.
Source
Thrown at crates/backtest/src/python/modules.rs:296
fn process(
&self,
ts_now: nautilus_core::UnixNanos,
ctx: &ExchangeContext,
) -> anyhow::Result<SimulationModuleResult> {
Python::attach(|py| -> anyhow::Result<SimulationModuleResult> {
let context = Py::new(py, PySimulationModuleContext::from_exchange(ctx))?;
let adjustments = self
.obj
.bind(py)
.call_method1("process", (ts_now.as_u64(), context))?
.extract::<Option<Vec<Money>>>()?;
Ok(adjustments.map_or(
SimulationModuleResult::NotReady,
SimulationModuleResult::Completed,
))
})
.map_err(|e| anyhow::anyhow!("Python SimulationModule.process failed: {e}"))
}
fn acknowledge(&self, outcomes: &[AccountAdjustmentOutcome]) -> anyhow::Result<()> {
Python::attach(|py| -> anyhow::Result<()> {
let outcomes = outcomes
.iter()
.map(PyAccountAdjustmentOutcome::from)
.collect::<Vec<_>>();
self.obj.bind(py).call_method1("acknowledge", (outcomes,))?;
Ok(())
})
.map_err(|e| anyhow::anyhow!("Python SimulationModule.acknowledge failed: {e}"))
}
fn log_diagnostics(&self) -> anyhow::Result<()> {
Python::attach(|py| -> anyhow::Result<()> {
self.obj.bind(py).call_method0("log_diagnostics")?;
Ok(())View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the embedded Python traceback and fix the exception in the module's process method.
- Handle edge cases: empty/None inputs, first-tick state, zero quantities.
- Reproduce with a minimal data slice (single tick/bar) to debug quickly.
Example fix
# before
def process(self, ts_now, data):
return self.book[data.instrument_id].best_bid() # KeyError
# after
def process(self, ts_now, data):
book = self.book.get(data.instrument_id)
return book.best_bid() if book else None Defensive patterns
Strategy: try-catch
Validate before calling
module.process(0, sample_data, sample_ctx) # smoke-test edge inputs before the run
Try / catch
class SafeModule(SimulationModule):
def process(self, ts_now, data, ctx):
try:
return self._process(ts_now, data, ctx)
except Exception:
logging.exception("SimulationModule.process failed at ts=%s", ts_now)
raise
Prevention
- Guard against empty books, zero quantities, and first-tick states in process().
- Reproduce failures with a minimal single-tick data slice.
- Keep per-instrument state in dicts accessed via .get() with defaults.
When it happens
Trigger: The Python object's process(ts_now, data, ...) raises during exchange processing (e.g. arithmetic on None, missing account state, exceptions in custom liquidity/fill logic).
Common situations: Custom fee/latency/fill modules hitting unhandled market states (first tick, empty book, halted instrument); division by zero in slippage calculations.
Related errors
- Python SimulationModule.pre_process failed: {e}
- Python SimulationModule.acknowledge failed: {e}
- Python SimulationModule.log_diagnostics failed: {e}
- Python SimulationModule.reset failed: {e}
- BacktestEngineConfig.controller for importable controller '{
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/384e46c3a35ea896.
Report an issue: GitHub.