nautechsystems/nautilus_trader · error

Python on_pool failed

Error message

Python on_pool failed: {e}

What it means

This error wraps any Python exception raised inside the actor's `on_pool` callback when a DeFi `Pool` update is dispatched to the Python subclass (only compiled with the `defi` feature). The Rust `PyDataActor` clones the pool, converts it via `into_py_any`, and calls the Python method via `call_method1`; a resulting `PyErr` is converted to an anyhow error with this message. The underlying exception is raised in the user's Python override or during conversion.

Solutions

  1. Read the appended Python traceback after 'Python on_pool failed:' to locate the raising line.
  2. Guard against zero/None liquidity and price fields before division or comparison.
  3. Register the pool instrument before processing its updates so lookups succeed.
  4. Verify Pool field names/types against the installed nautilus_trader version.

Example fix

// before
def on_pool(self, pool):
    price = pool.token0_reserve / pool.token1_reserve

// after
def on_pool(self, pool):
    if not pool.token1_reserve:
        return
    price = pool.token0_reserve / pool.token1_reserve
Defensive patterns

Strategy: validation

Validate before calling

r0, r1 = pool.token0_reserve, pool.token1_reserve
if not r0 or not r1:
    return  # zero liquidity or uninitialized pool

Type guard

def is_tradeable_pool(pool):
    r0 = getattr(pool, "token0_reserve", 0)
    r1 = getattr(pool, "token1_reserve", 0)
    return r0 and r1 and r0 > 0 and r1 > 0

Try / catch

def on_pool(self, pool):
    try:
        self._track_pool(pool)
    except Exception as e:
        self.log.error(f"on_pool failed: {e}", exc_info=True)

Prevention

When it happens

Trigger: Any exception raised by the user's `on_pool(self, pool)` override — e.g. Decimal division by pool liquidity of zero, dict lookups on an unseen pool address/instrument_id, or a raising Rust-to-Python conversion of Pool.

Common situations: AMM/LP strategies tracking pools that encounter a freshly created or drained pool with zero liquidity; handlers written for an older Pool binding before an upgrade; pool identifiers changing format between chain deployments.

Related errors


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

Appendix: source

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

        self.dispatch_on_option_greeks(*greeks)
            .map_err(|e| anyhow::anyhow!("Python on_option_greeks failed: {e}"))
    }

    fn on_option_chain(&mut self, slice: &OptionChainSlice) -> anyhow::Result<()> {
        self.dispatch_on_option_chain(slice.clone())
            .map_err(|e| anyhow::anyhow!("Python on_option_chain failed: {e}"))
    }

    #[cfg(feature = "defi")]
    fn on_block(&mut self, block: &Block) -> anyhow::Result<()> {
        self.dispatch_on_block(block.clone())
            .map_err(|e| anyhow::anyhow!("Python on_block failed: {e}"))
    }

    #[cfg(feature = "defi")]
    fn on_pool(&mut self, pool: &Pool) -> anyhow::Result<()> {
        self.dispatch_on_pool(pool.clone())
            .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}"))

View on GitHub (pinned to 18893faf8b)