nautechsystems/nautilus_trader · error

Python on_resume failed: {e}

Error message

Python on_resume failed: {e}

What it means

Raised by the Rust `DataActor` bridge when the Python actor's `on_resume()` callback throws an exception. `dispatch_on_resume()` propagates the Python error and `map_err` rewraps it with this message. It fires when the engine resumes a previously degraded or stopped component and delegates to user Python code.

Source

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

        }
    });
    msgbus::register_any(endpoint.into(), handler);
}

impl DataActor for PyDataActorInner {
    fn on_start(&mut self) -> anyhow::Result<()> {
        self.dispatch_on_start()
            .map_err(|e| anyhow::anyhow!("Python on_start failed: {e}"))
    }

    fn on_stop(&mut self) -> anyhow::Result<()> {
        self.dispatch_on_stop()
            .map_err(|e| anyhow::anyhow!("Python on_stop failed: {e}"))
    }

    fn on_resume(&mut self) -> anyhow::Result<()> {
        self.dispatch_on_resume()
            .map_err(|e| anyhow::anyhow!("Python on_resume failed: {e}"))
    }

    fn on_reset(&mut self) -> anyhow::Result<()> {
        self.dispatch_on_reset()
            .map_err(|e| anyhow::anyhow!("Python on_reset failed: {e}"))
    }

    fn on_dispose(&mut self) -> anyhow::Result<()> {
        self.dispatch_on_dispose()
            .map_err(|e| anyhow::anyhow!("Python on_dispose failed: {e}"))
    }

    fn on_degrade(&mut self) -> anyhow::Result<()> {
        self.dispatch_on_degrade()
            .map_err(|e| anyhow::anyhow!("Python on_degrade failed: {e}"))
    }

    fn on_fault(&mut self) -> anyhow::Result<()> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the Python traceback contained in the error message to locate the raising line in on_resume.
  2. Wrap risky re-subscription/reconnection logic in on_resume with try/except and log-and-handle.
  3. Ensure any state on_resume reads is initialized in on_start/on_init so resume after reset works.
  4. Test the degrade->resume cycle locally before running it live.

Example fix

// before (Python)
def on_resume(self):
    self.subscribe_bars(self.bar_type)

// after
def on_resume(self):
    try:
        self.subscribe_bars(self.bar_type)
    except Exception as e:
        self.log.error(f"resubscribe failed: {e}")
Defensive patterns

Strategy: try-catch

Validate before calling

# before resuming, check prerequisites
assert getattr(self, 'bar_type', None) is not None, 'bar_type must be set before resume'

Type guard

def can_resume(actor):
    return callable(getattr(actor, 'on_resume', None)) and getattr(actor, 'initialized', False)

Try / catch

def on_resume(self):
    try:
        ...  # resubscribe/reconnect
    except Exception as e:
        self.log.error(f"on_resume failed: {e}")

Prevention

When it happens

Trigger: Calling `actor.resume()` / trading-node resume flow where the user's Python `on_resume` raises: re-subscribing to data feeds with an invalid config, using state that was never initialized, or any runtime exception in the callback body.

Common situations: Recovery after degradation in live trading where on_resume reconnects to a venue and the connection call raises; resuming after on_reset cleared state that on_resume assumes exists.

Related errors


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