{"record":{"id":"fa8c7be6d65a3d49","repo":"nautechsystems/nautilus_trader","slug":"python-on-pool-swap-failed-e","errorCode":null,"errorMessage":"Python on_pool_swap failed: {e}","messagePattern":"Python on_pool_swap failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/python/actor.rs","lineNumber":1181,"sourceCode":"            .map_err(|e| anyhow::anyhow!(\"Python on_option_chain failed: {e}\"))\n    }\n\n    #[cfg(feature = \"defi\")]\n    fn on_block(&mut self, block: &Block) -> anyhow::Result<()> {\n        self.dispatch_on_block(block.clone())\n            .map_err(|e| anyhow::anyhow!(\"Python on_block failed: {e}\"))\n    }\n\n    #[cfg(feature = \"defi\")]\n    fn on_pool(&mut self, pool: &Pool) -> anyhow::Result<()> {\n        self.dispatch_on_pool(pool.clone())\n            .map_err(|e| anyhow::anyhow!(\"Python on_pool failed: {e}\"))\n    }\n\n    #[cfg(feature = \"defi\")]\n    fn on_pool_swap(&mut self, swap: &PoolSwap) -> anyhow::Result<()> {\n        self.dispatch_on_pool_swap(swap.clone())\n            .map_err(|e| anyhow::anyhow!(\"Python on_pool_swap failed: {e}\"))\n    }\n\n    #[cfg(feature = \"defi\")]\n    fn on_pool_liquidity_update(&mut self, update: &PoolLiquidityUpdate) -> anyhow::Result<()> {\n        self.dispatch_on_pool_liquidity_update(update.clone())\n            .map_err(|e| anyhow::anyhow!(\"Python on_pool_liquidity_update failed: {e}\"))\n    }\n\n    #[cfg(feature = \"defi\")]\n    fn on_pool_fee_collect(&mut self, collect: &PoolFeeCollect) -> anyhow::Result<()> {\n        self.dispatch_on_pool_fee_collect(collect.clone())\n            .map_err(|e| anyhow::anyhow!(\"Python on_pool_fee_collect failed: {e}\"))\n    }\n\n    #[cfg(feature = \"defi\")]\n    fn on_pool_flash(&mut self, flash: &PoolFlash) -> anyhow::Result<()> {\n        self.dispatch_on_pool_flash(flash.clone())\n            .map_err(|e| anyhow::anyhow!(\"Python on_pool_flash failed: {e}\"))","sourceCodeStart":1163,"sourceCodeEnd":1199,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/python/actor.rs#L1163-L1199","documentation":"This error is raised by the Rust `DataActor::on_pool_swap` wrapper (crates/common/src/python/actor.rs:1181) when dispatching a `PoolSwap` DeFi event to the Python actor's `on_pool_swap` callback fails. The underlying `dispatch_on_pool_swap` calls `py_self.call_method1(py, \"on_pool_swap\", ...)` via pyo3, so any Python exception raised inside the user's handler, a missing/incorrectly-named handler method, or a failure converting the `PoolSwap` to a Python object (`.into_py_any`) surfaces here wrapped as `Python on_pool_swap failed: {e}`. It is the library's way of propagating a Python-side callback failure back into the Rust actor pipeline.","triggerScenarios":"Occurs when `on_pool_swap(&mut self, swap: &PoolSwap)` is invoked and (1) the bound Python actor instance raises an exception inside its `on_pool_swap` method, (2) the Python object has no callable `on_pool_swap` attribute (pyo3 call_method1 fails), or (3) `swap.into_py_any(py)` fails to convert the PoolSwap into a Python object.","commonSituations":"A Python strategy subclasses the actor but overrides or renames `on_pool_swap` incorrectly, or the handler code itself throws (e.g. unpacking swap fields, math on amounts, calling a DEX/contract helper that fails). Also common when the defi feature data types changed between nautilus versions and user handler signatures no longer match, or when custom __init__ code leaves the actor partially initialized so the callback errors.","solutions":["Inspect the chained `{e}` message in the log; it contains the original Python traceback/exception raised inside your `on_pool_swap` handler - fix that root cause first.","Verify your Python actor class defines a callable `on_pool_swap(self, swap)` method with the correct signature and no typos in the name.","Add defensive error handling/logging inside the Python handler and test it directly with a synthetic PoolSwap before running the live/backtest pipeline.","Confirm your nautilus version's PoolSwap fields match what the handler accesses; update handler code after upgrading the crate."],"exampleFix":"# before: handler raises on unexpected None\ndef on_pool_swap(self, swap):\n    amount = int(swap.amount)  # raises if amount is None/unexpected\n\n# after: validate inputs defensively\ndef on_pool_swap(self, swap):\n    if swap.amount is None:\n        self.log.warning(\"PoolSwap missing amount, skipping\")\n        return\n    amount = int(swap.amount)","handlingStrategy":"try-catch","validationCode":"assert callable(getattr(actor, 'on_pool_swap', None)), \"actor must implement on_pool_swap\"\n","typeGuard":"def has_pool_swap_handler(obj):\n    return callable(getattr(obj, 'on_pool_swap', None))\n","tryCatchPattern":"try:\n    actor.on_pool_swap(swap)\nexcept Exception as e:\n    log.error(f\"on_pool_swap handler failed: {e}\", exc_info=True)  # inspect chained cause\n","preventionTips":["Always implement on_pool_swap on any actor subscribing to pool swap data","Validate swap fields (amount, amounts in/out) at the top of the handler","Unit-test the handler with synthetic PoolSwap events","Re-check handler field access after every nautilus upgrade"],"tags":["python","defi","callback","pyo3","rust"],"backgroundTag":"api-request-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}