nautechsystems/nautilus_trader · error · anyhow::Error

Cannot send execution report: sender not initialized

Error message

Cannot send execution report: sender not initialized

What it means

Raised by try_send_execution_report when the emitter's sender slot is still None: an ExecutionReport (order status, fills, etc.) cannot be routed because the execution event channel was never wired. This is a lifecycle/ordering violation in the live execution pipeline.

Source

Thrown at crates/live/src/execution/emitter.rs:520

            .map_err(|e| anyhow::anyhow!("Failed to send account state: {e}"))
    }

    /// Emits an execution report.
    pub fn send_execution_report(&self, report: ExecutionReport) {
        if let Err(e) = self.try_send_execution_report(report) {
            log::warn!("{e}");
        }
    }

    /// Emits an execution report and returns any channel error to the caller.
    ///
    /// # Errors
    ///
    /// Returns an error if the sender is not initialized or the receiving channel is closed.
    pub fn try_send_execution_report(&self, report: ExecutionReport) -> anyhow::Result<()> {
        let sender = self.sender.load();
        let sender = sender.as_ref().ok_or_else(|| {
            anyhow::anyhow!("Cannot send execution report: sender not initialized")
        })?;
        sender
            .send(ExecutionEvent::Report(report))
            .map_err(|e| anyhow::anyhow!("Failed to send execution report: {e}"))
    }

    /// Emits an order status report.
    pub fn send_order_status_report(&self, report: OrderStatusReport) {
        self.send_execution_report(ExecutionReport::Order(Box::new(report)));
    }

    /// Emits a fill report.
    pub fn send_fill_report(&self, report: FillReport) {
        self.send_execution_report(ExecutionReport::Fill(Box::new(report)));
    }

    /// Emits an order status report bundled with the fills that produced it.
    pub fn send_order_with_fills(&self, report: OrderStatusReport, fills: Vec<FillReport>) {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Complete the emitter/engine initialization before processing any venue messages.
  2. Buffer incoming reports until the emitter is ready, then flush in order.
  3. Verify adapter message handlers are only registered/active after the engine start sequence.
  4. If it happens in a running node, this indicates an internal bug — capture a repro and report it.

Example fix

// before
emitter.send_order_status_report(report);
// after
if emitter.is_initialized() {
    emitter.send_order_status_report(report);
} else {
    pending_reports.push(report); // flush after init
}
Defensive patterns

Strategy: validation

Validate before calling

if !emitter.is_initialized() { pending_reports.push(report); return; }

Type guard

fn emitter_ready(emitter: &ExecutionEmitter) -> bool { emitter.is_initialized() }

Try / catch

if let Err(e) = emitter.try_send_execution_report(report) { log::error!("execution report send failed: {e}"); }

Prevention

When it happens

Trigger: Calling try_send_execution_report (directly or via send_execution_report, send_order_status_report, etc., or commit_post_reconnect_mass_status) before the emitter is initialized.

Common situations: Adapters producing execution reports from venue WebSocket messages that arrive before engine startup completes; mass-status reconciliation after reconnect racing the emitter init; unit/custom integration harnesses forgetting to start the emitter.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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