nautechsystems/nautilus_trader · error

Failed to extract PyDataActor: {e}

Error message

Failed to extract PyDataActor: {e}

What it means

Raised when Rust code cannot extract a `PyRefMut<PyDataActor>` from a Python object passed as an actor during actor registration (registering the actor and reading its id). `extract` fails when the Python object is not actually an instance of the `PyDataActor` class (or a subclass) expected by the Rust binding, so registration aborts with this anyhow-wrapped error.

Source

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

/// Applies optional config overrides and stores a weak reference to the Python instance used for
/// method dispatch. Missing or unreadable attributes are ignored, and non-boolean `log_events` or
/// `log_commands` values leave the existing settings unchanged. When neither the config nor
/// `DataActor.__init__` supplies an ID, the runtime class name replaces the shared default so
/// subclasses that skip the base initializer still receive distinct IDs.
///
/// # Errors
///
/// Returns an error if the actor cannot be extracted, its configured `actor_id` is neither an
/// [`ActorId`] nor a valid ID string, its class-derived ID is invalid, or its Python instance
/// cannot be weakly referenced.
pub fn prepare_python_actor(
    actor_obj: &Bound<'_, PyAny>,
    config: Option<&Bound<'_, PyAny>>,
) -> anyhow::Result<ActorId> {
    let mut actor = actor_obj
        .extract::<PyRefMut<PyDataActor>>()
        .map_err(Into::<PyErr>::into)
        .map_err(|e| anyhow::anyhow!("Failed to extract PyDataActor: {e}"))?;

    if let Some(config) = config {
        if let Some(actor_id) = config
            .getattr("actor_id")
            .ok()
            .filter(|actor_id| !actor_id.is_none())
        {
            let actor_id = if let Ok(actor_id) = actor_id.extract::<ActorId>() {
                actor_id
            } else if let Ok(actor_id) = actor_id.extract::<String>() {
                ActorId::new_checked(&actor_id)?
            } else {
                anyhow::bail!("Invalid `actor_id` type");
            };
            actor.set_actor_id(actor_id);
        }

        if let Some(log_events) = extract_bool_config_attr(config, "log_events") {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the object passed to registration is created via the correct Python factory/base class that produces a `PyDataActor`-backed object (subclass the provided Actor/DataActor base, do not pass arbitrary objects).
  2. Check that you are passing the actor object as the correct argument (not swapped with config) to the registration call.
  3. Verify you are not mixing versions of the library where the actor class layout changed; reinstall/rebuild the package consistently.
  4. Print `type(actor_obj)` and its MRO in Python to confirm it derives from the expected base before registering.

Example fix

# before
actor = MyCustomThing()          # not a PyDataActor-backed instance
register_actor(actor, config)

# after
from nautilus_trader.common.actor import Actor

class MyActor(Actor):            # backed by PyDataActor on the Rust side
    ...

actor = MyActor(config=config)
register_actor(actor, config)
Defensive patterns

Strategy: type-guard

Validate before calling

# Python, before registration
from nautilus_trader.common.actor import Actor
assert isinstance(actor, Actor), f'{type(actor)} is not a supported Actor type'

Type guard

def is_registrable_actor(obj) -> bool:
    from nautilus_trader.common.actor import Actor
    return isinstance(obj, Actor)

Try / catch

match register_actor(&actor_obj, config) {
    Err(e) => eprintln!("actor registration failed: {e:#}"),
    Ok(id) => println!("registered actor {id}"),
}

Prevention

When it happens

Trigger: Passing a Python object to the actor registration function (crates/common/src/python/actor.rs:2842) that is not a `PyDataActor` subclass — e.g. a plain Actor/Strategy class not derived from the required Python base, a wrong wrapper, or passing None/misordered arguments so the wrong object lands in `actor_obj`.

Common situations: Mixing actor base classes across API versions (actor created from the wrong factory or module), forgetting to subclass the required Python `Actor`/`DataActor` base that is backed by `PyDataActor`, or refactors that changed the registration entry point while callers still pass legacy objects.

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/4a33a030671aded0. Report an issue: GitHub.