nautechsystems/nautilus_trader · error · anyhow::Error
Failed to send order denied event: {e}
Error message
Failed to send order denied event: {e} What it means
When the adapter converts an IB order-denied notification into an `OrderEventAny::Denied` event, it sends the event over the global `tokio::sync::mpsc::UnboundedSender` obtained from `get_exec_event_sender()`. If the receiver side has been shut down (the execution event loop dropped its receiver), `send` fails and this anyhow error is returned, so the denial cannot be surfaced to the strategy.
Source
Thrown at crates/adapters/interactive_brokers/src/execution/core.rs:1926
instrument_id: InstrumentId,
client_order_id: ClientOrderId,
reason: &str,
) -> anyhow::Result<()> {
let ts_event = get_atomic_clock_realtime().get_time_ns();
let event = OrderDenied::new(
trader_id,
strategy_id,
instrument_id,
client_order_id,
Ustr::from(reason),
UUID4::new(),
ts_event,
ts_event,
);
get_exec_event_sender()
.send(ExecutionEvent::Order(OrderEventAny::Denied(event)))
.map_err(|e| anyhow::anyhow!("Failed to send order denied event: {e}"))
}
fn send_order_modify_rejected(
cmd: &ModifyOrder,
reason: &str,
exec_sender: &tokio::sync::mpsc::UnboundedSender<ExecutionEvent>,
ts_event: UnixNanos,
account_id: AccountId,
) -> anyhow::Result<()> {
let event = OrderModifyRejected::new(
cmd.trader_id,
cmd.strategy_id,
cmd.instrument_id,
cmd.client_order_id,
Ustr::from(reason),
UUID4::new(),
ts_event,
ts_event,View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the node's execution event loop/actor stays alive as long as the IB adapter can deliver events; shut down the adapter before dropping the engine.
- Check that `get_exec_event_sender()` is wired to the correct receiver and nothing re-initialized the global channel.
- If this occurs at shutdown, treat it as benign log noise, but drain in-flight events before stopping the event loop.
Example fix
// before
let sender = get_exec_event_sender();
sender.send(event).map_err(...)?;
// after
if let Err(e) = get_exec_event_sender().send(event) {
log::warn!("exec event dropped (receiver closed): {e}");
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before shutdown-sensitive work, confirm the exec channel still has a receiver
fn exec_channel_alive() -> bool { /* true if the execution event loop task is still running */ true } Try / catch
if let Err(e) = client.submit_order(&cmd).await {
if e.to_string().contains("Failed to send order denied event") {
log::warn!("exec receiver closed; order denial lost: {e}");
} else {
return Err(e);
}
} Prevention
- Disconnect the IB adapter before dropping the execution engine during shutdown.
- Never drop the exec event receiver while adapter tasks may still emit events.
- Keep a single canonical global exec channel; avoid re-initializing it mid-run.
When it happens
Trigger: An order denied by IB arrives (e.g. rejected at submit due to margin/limits) while the execution event receiver has been dropped — i.e. during shutdown, after the actor/event-loop stopped, or a miswired exec event channel.
Common situations: Node shutting down while a rejection arrives in flight; TWS emitting a late deny after `ExecutionEngine` teardown; tests or tools that drop the receiver early.
Related errors
- Failed to send order modify rejected event: {e}
- Failed to send order cancel rejected event: {e}
- Failed to send order pending cancel event: {e}
- task slot is already occupied
- failed to finish Binance Futures data command tasks: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/68d2fab5a747d542.
Report an issue: GitHub.