nautechsystems/nautilus_trader · error

Python on_pool_fee_collect failed: {e}

Error message

Python on_pool_fee_collect failed: {e}

What it means

Raised by `DataActor::on_pool_fee_collect` (crates/common/src/python/actor.rs:1193) when dispatching a `PoolFeeCollect` event to the Python actor's `on_pool_fee_collect` callback fails. The dispatch uses pyo3 `call_method1` on the Python instance, so any exception raised in the Python handler, an absent/uncallable method, or a `PoolFeeCollect` -> Python conversion failure is wrapped as `Python on_pool_fee_collect failed: {e}`.

Source

Thrown at crates/common/src/python/actor.rs:1193

            .map_err(|e| anyhow::anyhow!("Python on_pool failed: {e}"))
    }

    #[cfg(feature = "defi")]
    fn on_pool_swap(&mut self, swap: &PoolSwap) -> anyhow::Result<()> {
        self.dispatch_on_pool_swap(swap.clone())
            .map_err(|e| anyhow::anyhow!("Python on_pool_swap failed: {e}"))
    }

    #[cfg(feature = "defi")]
    fn on_pool_liquidity_update(&mut self, update: &PoolLiquidityUpdate) -> anyhow::Result<()> {
        self.dispatch_on_pool_liquidity_update(update.clone())
            .map_err(|e| anyhow::anyhow!("Python on_pool_liquidity_update failed: {e}"))
    }

    #[cfg(feature = "defi")]
    fn on_pool_fee_collect(&mut self, collect: &PoolFeeCollect) -> anyhow::Result<()> {
        self.dispatch_on_pool_fee_collect(collect.clone())
            .map_err(|e| anyhow::anyhow!("Python on_pool_fee_collect failed: {e}"))
    }

    #[cfg(feature = "defi")]
    fn on_pool_flash(&mut self, flash: &PoolFlash) -> anyhow::Result<()> {
        self.dispatch_on_pool_flash(flash.clone())
            .map_err(|e| anyhow::anyhow!("Python on_pool_flash failed: {e}"))
    }

    fn on_historical_data(&mut self, data: &dyn Any) -> anyhow::Result<()> {
        Python::attach(|py| {
            let py_data: Py<PyAny> = if let Some(custom_data) = data.downcast_ref::<CustomData>() {
                Py::new(py, custom_data.clone())?.into_any()
            } else if let Some(custom_data) = data.downcast_ref::<Vec<CustomData>>() {
                custom_data.clone().into_py_any(py)?
            } else {
                anyhow::bail!("Failed to convert historical data to Python: unsupported type");
            };
            self.dispatch_on_historical_data(py_data)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Examine the embedded `{e}` cause (Python exception/traceback) and fix the failing code in `on_pool_fee_collect`.
  2. Confirm the Python actor defines `on_pool_fee_collect(self, collect)` with a matching signature.
  3. Validate/normalize amounts and currency fields inside the handler before doing fee accounting math.
  4. Align field access with the PoolFeeCollect type of your installed nautilus version.

Example fix

# before: assumes fields always present
def on_pool_fee_collect(self, collect):
    self.accrue(collect.amount0 + collect.amount1)

# after
def on_pool_fee_collect(self, collect):
    if collect.amount0 is None or collect.amount1 is None:
        self.log.warning("Incomplete PoolFeeCollect, skipping")
        return
    self.accrue(collect.amount0 + collect.amount1)
Defensive patterns

Strategy: try-catch

Validate before calling

assert callable(getattr(actor, 'on_pool_fee_collect', None)), "actor must implement on_pool_fee_collect"

Type guard

def has_fee_collect_handler(obj):
    return callable(getattr(obj, 'on_pool_fee_collect', None))

Try / catch

try:
    actor.on_pool_fee_collect(collect)
except Exception as e:
    log.error(f"on_pool_fee_collect failed: {e}", exc_info=True)

Prevention

When it happens

Trigger: Fires when a pool fee-collection event is delivered to a defi actor and the Python callback raises an exception, the method does not exist on the Python object, or `collect.into_py_any(py)` fails during conversion.

Common situations: Fee-accounting code in the handler throwing (None amounts, Decimal/int mixups, currency lookup failures); subscribing to fee collect events without implementing the handler; mismatches after crate upgrades that renamed event fields; handler calling external services that fail.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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