nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send order rejected event: {e}

Error message

Failed to send order rejected event: {e}

What it means

After a what-if order analysis completes, the adapter builds an OrderRejected/Denied event and sends it through the exec_sender channel. The send fails when the execution event receiver has been dropped, meaning the engine consuming events is gone while the IB what-if response is being processed.

Source

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

            .unwrap_or_else(|_| format!("whatIf analysis for order {}", order_data.order_id));

        let event = OrderRejected::new(
            trader_id,
            strategy_id,
            instrument_id,
            client_order_id,
            account_id,
            Ustr::from(&reason_json),
            UUID4::new(),
            ts_init,
            ts_init,
            false,
            false,
        );

        exec_sender
            .send(ExecutionEvent::Order(OrderEventAny::Rejected(event)))
            .map_err(|e| anyhow::anyhow!("Failed to send order rejected event: {e}"))?;

        tracing::debug!(
            "What-if analysis completed for order {}: margin change={:?}, commission={:?}",
            client_order_id,
            order_data
                .order_state
                .initial_margin_after
                .and_then(|after| order_data
                    .order_state
                    .initial_margin_before
                    .map(|before| after - before)),
            order_data.order_state.commission
        );

        Ok(())
    }

    #[allow(clippy::too_many_arguments)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Keep the execution engine/event consumer running until IB what-if responses are drained or the adapter disconnects.
  2. Stop what-if requests and adapter subscriptions before shutting down the event receiver.
  3. Log send failures during shutdown rather than treating them as fatal.

Example fix

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

Strategy: try-catch

Validate before calling

// Rust: only send what-if events while the channel is open
if exec_sender.is_closed() {
    tracing::warn!("Channel closed; what-if rejected event dropped");
    return Ok(());
}

Type guard

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

Try / catch

if let Err(e) = exec_sender.send(ExecutionEvent::Order(OrderEventAny::Rejected(event))) {
    tracing::warn!("What-if rejected event dropped: {e}");
    return Ok(());
}

Prevention

When it happens

Trigger: handle_whatif_order receives an OpenOrder/OrderState for a what-if order, emits OrderEventAny::Rejected via exec_sender.send, and the channel receiver is closed.

Common situations: Engine/node shut down while what-if results stream back; consumer task died from an earlier fault; adapter still subscribed after teardown.

Related errors


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