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
- Ensure the execution event consumer (ExecutionEngine) is kept alive as long as the IB adapter is connected and subscribed to open orders.
- Shut down the IB adapter's order stream before dropping the event receiver, so no sends happen after teardown.
- 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
- Tie adapter lifetime to the execution engine lifetime so receivers are never dropped first
- Use scoped shutdown ordering: stop venue streams, then drop the event receiver
- Monitor channel-closed warnings as a signal of shutdown-order bugs
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
- Failed to send order canceled event: {e}
- Failed to send order rejected event: {e}
- failed to request logging sync: {e}
- failed to receive logging sync acknowledgement: {e}
- Failed to send delete order command: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/569b0456bb48dc97.
Report an issue: GitHub.