nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send order updated event: {e}

Error message

Failed to send order updated event: {e}

What it means

The Interactive Brokers execution adapter failed to deliver an OrderUpdated event through the internal exec_sender mpsc channel. This means the receiving side of the channel has been dropped or closed while the adapter was processing an IB OpenOrder callback. It indicates the execution event pipeline is torn down but the adapter is still receiving venue updates.

Source

Thrown at crates/adapters/interactive_brokers/src/execution/core_updates.rs:429

            strategy_id,
            instrument_id,
            client_order_id,
            quantity,
            UUID4::new(),
            ts_init,
            ts_init,
            false,
            Some(venue_order_id),
            Some(account_id),
            price,
            trigger_price,
            None,
            false,
        );

        exec_sender
            .send(ExecutionEvent::Order(OrderEventAny::Updated(event)))
            .map_err(|e| anyhow::anyhow!("Failed to send order updated event: {e}"))
    }

    fn open_order_price_fields(
        order_data: &ibapi::orders::OrderData,
        price_magnifier: f64,
        price_precision: u8,
    ) -> (Option<Price>, Option<Price>) {
        let order_type = IbOrderType::from_str(order_data.order.order_type.as_str())
            .map_or(OrderType::Market, IbOrderType::nautilus_order_type);
        let price = order_data
            .order
            .limit_price
            .map(|price| Price::new(price * price_magnifier, price_precision));
        let trigger_price = order_data
            .order
            .aux_price
            .map(|price| Price::new(price * price_magnifier, price_precision));

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the execution event consumer (ExecutionEngine) is kept alive as long as the IB adapter is connected and subscribed to open orders.
  2. Shut down the IB adapter's order stream before dropping the event receiver, so no sends happen after teardown.
  3. If shutdown is intentional, downgrade handling of this Err to a log rather than propagating it as a fatal error.

Example fix

// before
exec_sender
    .send(ExecutionEvent::Order(OrderEventAny::Updated(event)))
    .map_err(|e| anyhow::anyhow!("Failed to send order updated event: {e}"))
// after
if let Err(e) = exec_sender.send(ExecutionEvent::Order(OrderEventAny::Updated(event))) {
    tracing::warn!("Execution channel closed, dropping order updated event: {e}");
    return Ok(());
}
Ok(())
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: check channel liveness before producing events
if exec_sender.is_closed() {
    tracing::warn!("Execution event channel closed; not emitting order updated event");
    return Ok(());
}

Type guard

fn can_send(sender: &mpsc::Sender<ExecutionEvent>) -> bool {
    !sender.is_closed()
}

Try / catch

match exec_sender.send(event) {
    Ok(()) => {}
    Err(e) => tracing::warn!("Order updated event dropped, channel closed: {e}"),
}

Prevention

When it happens

Trigger: emit_order_updated_from_open_order calls exec_sender.send(ExecutionEvent::Order(OrderEventAny::Updated(event))) and the receiver half of the channel has been dropped (e.g. the ExecutionEngine or the task consuming execution events was shut down) so send returns Err.

Common situations: Engine shutdown while IB still streams open-order updates; a panicking or prematurely-stopped consumer task; reconnect storms where the adapter outlives the event receiver.

Related errors


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