nautechsystems/nautilus_trader · error
Execution event sender should be initialized by runner
Error message
Execution event sender should be initialized by runner
What it means
`get_exec_event_sender` clones the thread-local tokio mpsc sender used to push ExecutionEvents from clients into the live runner. It panics with this message when EXEC_EVENT_SENDER has not been initialized by the runner on the current thread. This is an internal invariant: event-producing code (order submission, account state generation, position subscriptions) assumes the runner installed the channel.
Source
Thrown at crates/common/src/live/runner.rs:175
/// Replaces the system command sender for the current thread.
pub fn replace_system_command_sender(sender: tokio::sync::mpsc::UnboundedSender<SystemCommand>) {
SYSTEM_COMMAND_SENDER.with(|s| {
*s.borrow_mut() = Some(sender);
});
}
/// Gets the thread-local execution event sender.
///
/// # Panics
///
/// Panics if the sender is uninitialized.
#[must_use]
pub fn get_exec_event_sender() -> tokio::sync::mpsc::UnboundedSender<ExecutionEvent> {
EXEC_EVENT_SENDER.with(|sender| {
sender
.borrow()
.as_ref()
.expect("Execution event sender should be initialized by runner")
.clone()
})
}
/// Attempts to get the thread-local execution event sender without panicking.
///
/// Returns `None` if the sender is not initialized (e.g., in test environments).
#[must_use]
pub fn try_get_exec_event_sender() -> Option<tokio::sync::mpsc::UnboundedSender<ExecutionEvent>> {
EXEC_EVENT_SENDER.with(|sender| sender.borrow().as_ref().cloned())
}
/// Sets the thread-local execution event sender.
///
/// Can only be called once per thread.
///
/// # Panics
///View on GitHub (pinned to 18893faf8b)
Solutions
- Start the live runner (or call its sender-binding init) before any client/event-producing call
- In tests, bind a test exec-event channel first (the pattern used by runner tests)
- Keep event emission on the same OS thread where the runner initialized the thread-local
- Use the non-panicking getter variant if the sender may legitimately be absent
Example fix
// before let tx = get_exec_event_sender(); // after runner_bind_exec_sender(); // runner startup let tx = get_exec_event_sender();
Defensive patterns
Strategy: validation
Validate before calling
if !runner_senders_initialized() {
eprintln!("runner must initialize exec event sender first");
return;
}
let sender = get_exec_event_sender(); Type guard
fn exec_sender_ready() -> bool {
try_exec_event_sender().is_some()
} Try / catch
// Guard with the non-panicking getter
if let Some(sender) = try_get_exec_event_sender() { /* emit event */ } else { init_runner(); } Prevention
- Bind the exec-event channel (runner start) before submitting orders or subscribing to positions
- Prefer the non-panicking getter in adapter code where startup order may vary
- Ensure event emission happens on the initializing thread
- In test setups, install a test channel before exercising order/account APIs
When it happens
Trigger: Calling `get_exec_event_sender()` or any wrapper (start, submit_order, submit_order_list_with_orders, generate_account_state, query_order, subscribe_positions) before the runner bound the exec-event channel on this thread.
Common situations: Bypassing `LiveNode::build()`/start; custom adapters emitting execution events in tests without binding a sender; running the emitting code on another thread than the one the runner initialized.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- System command sender should be initialized by runner
- task slot is already occupied
- Data event sender should be initialized by runner
- System event sender should be initialized by runner
- registered callback lease was not acquired
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/16accd20e12f20cc.
Report an issue: GitHub.