nautechsystems/nautilus_trader · error

Python SimulationModule.acknowledge failed: {e}

Error message

Python SimulationModule.acknowledge failed: {e}

What it means

The Python SimulationModule's acknowledge method raised an exception when the exchange confirmed account adjustment outcomes (e.g. deposit/withdrawal results). The Rust wrapper maps the PyErr to this anyhow error.

Source

Thrown at crates/backtest/src/python/modules.rs:308

                .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(())
        })
        .map_err(|e| anyhow::anyhow!("Python SimulationModule.log_diagnostics failed: {e}"))
    }

    fn reset(&self) -> anyhow::Result<()> {
        Python::attach(|py| -> anyhow::Result<()> {
            self.obj.bind(py).call_method0("reset")?;
            Ok(())
        })
        .map_err(|e| anyhow::anyhow!("Python SimulationModule.reset failed: {e}"))
    }
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fix the exception shown in the Python traceback inside acknowledge.
  2. Make acknowledge idempotent/tolerant of unknown outcome IDs.
  3. Ensure reset() clears any per-adjustment state so acknowledgments stay in sync.

Example fix

# before
def acknowledge(self, outcomes):
    for o in outcomes:
        del self.pending[o.id]  # KeyError
# after
def acknowledge(self, outcomes):
    for o in outcomes:
        self.pending.pop(o.id, None)
Defensive patterns

Strategy: try-catch

Try / catch

def acknowledge(self, outcomes):
    try:
        for o in outcomes:
            self.pending.pop(o.id, None)
    except Exception:
        logging.exception("acknowledge failed")
        raise

Prevention

When it happens

Trigger: Calling exchange acknowledge flows where the Python module's acknowledge(outcomes) raises: unexpected outcome types, state mismatch (acknowledging an adjustment the module doesn't track).

Common situations: Modules that maintain ledger state desynchronized with the engine (e.g. after a reset or replay); KeyError on the outcome ID.

Related errors


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