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
- Examine the embedded `{e}` cause (Python exception/traceback) and fix the failing code in `on_pool_fee_collect`.
- Confirm the Python actor defines `on_pool_fee_collect(self, collect)` with a matching signature.
- Validate/normalize amounts and currency fields inside the handler before doing fee accounting math.
- 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
- Validate fee amounts and currency fields before accounting math
- Normalize Decimal/int types at handler entry
- Unit-test fee accrual with synthetic PoolFeeCollect events
- Diff handler field access against the event type after upgrades
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
- Python on_pool_swap failed: {e}
- Python on_pool_liquidity_update failed: {e}
- Python on_pool_flash failed: {e}
- Failed to convert historical data to Python: unsupported typ
- Failed to convert instrument to Python: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/473602d9558f5315.
Report an issue: GitHub.