nautechsystems/nautilus_trader · error

Failed to downcast to PyStrategy: {e}

Error message

Failed to downcast to PyStrategy: {e}

What it means

After registration, the code `cast::<PyStrategy>()`s the object to borrow it as the concrete Rust type and call `register_in_global_registries()`. A cast failure means the bound object is not actually a PyStrategy at the Rust level, and the error reports this downcast failure.

Source

Thrown at crates/system/src/python/registration.rs:320

                .map_err(|e| anyhow::anyhow!("Failed to extract PyStrategy: {e}"))?;

            py_strategy_ref
                .register(trader_id, clock, cache, portfolio)
                .map_err(|e| anyhow::anyhow!("Failed to register PyStrategy: {e}"))?;

            log::debug!(
                "Internal PyStrategy registered: {}",
                py_strategy_ref.is_registered()
            );

            Ok(())
        })?;

        Python::attach(|py| -> anyhow::Result<()> {
            let py_strategy = strategy.bind(py);
            let py_strategy_ref = py_strategy
                .cast::<PyStrategy>()
                .map_err(|e| anyhow::anyhow!("Failed to downcast to PyStrategy: {e}"))?;
            py_strategy_ref.borrow().register_in_global_registries()?;
            Ok(())
        })?;

        self.add_strategy_id_with_subscriptions::<PyStrategyInner>(strategy_id)
    }

    /// Adds a constructed [`PyExecutionAlgorithm`] instance to the trader.
    ///
    /// `wrapper` is the Python object which owns `algorithm`; the trader's registries keep it
    /// alive for as long as the algorithm stays registered.
    ///
    /// # Errors
    ///
    /// Returns an error if the trader already tracks a component under the algorithm's ID, or if
    /// the algorithm cannot be registered or tracked.
    pub fn add_py_execution_algorithm_instance(
        &mut self,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure only one nautilus_trader installation exists (`pip show nautilus_trader`, clean site-packages).
  2. Avoid `importlib.reload` of strategy modules that subclass the Rust-backed Strategy within a running process.
  3. Pass the strategy instance created from the same nautilus_trader import used by the trader.

Example fix

// before
import importlib, my_strats
importlib.reload(my_strats)  # creates a NEW Strategy subclass identity

// after
import my_strats  # import once per process; no reload after trader setup
Defensive patterns

Strategy: validation

Validate before calling

import sys
paths = [p for p in sys.path if "site-packages" in p]
assert len({p.split("/")[3] for p in paths}) >= 1  # inspect for duplicate installs
from nautilus_trader.trading.strategy import Strategy
assert isinstance(strategy, Strategy)

Type guard

def same_class_identity(obj) -> bool:
    from nautilus_trader.trading.strategy import Strategy
    return type(obj).__mro__[-2:] == Strategy.__mro__[-2:] or isinstance(obj, Strategy)

Try / catch

try:
    trader.add_strategy(strategy)
except Exception as e:
    if "downcast" in str(e):
        logging.error("Class identity mismatch — reload process or dedupe nautilus_trader installs")
    raise

Prevention

When it happens

Trigger: Registration of a strategy object that passed earlier steps but cannot be cast to the concrete `PyStrategy` class — typically a subclass whose PyO3 class identity differs (duplicate/reloaded module) or a non-strategy object reaching this path.

Common situations: Strategy classes defined in a module that was reloaded after nautilus_trader imported its Rust class; multiple nautilus_trader builds on sys.path creating two distinct PyStrategy classes.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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