{"record":{"id":"e8f8766182a7c25f","repo":"nautechsystems/nautilus_trader","slug":"data-event-sender-should-be-initialized-by-runner","errorCode":null,"errorMessage":"Data event sender should be initialized by runner","messagePattern":"Data event sender should be initialized by runner","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/live/runner.rs","lineNumber":35,"sourceCode":"//!\n//! This module provides thread-local storage for tokio mpsc channels used in live trading.\n\nuse std::cell::RefCell;\n\nuse crate::messages::{DataEvent, ExecutionEvent, SystemCommand, SystemEvent};\n\n/// Gets the thread-local data event sender.\n///\n/// # Panics\n///\n/// Panics if the sender is uninitialized.\n#[must_use]\npub fn get_data_event_sender() -> tokio::sync::mpsc::UnboundedSender<DataEvent> {\n    DATA_EVENT_SENDER.with(|sender| {\n        sender\n            .borrow()\n            .as_ref()\n            .expect(\"Data event sender should be initialized by runner\")\n            .clone()\n    })\n}\n\n/// Attempts to get the thread-local data event sender without panicking.\n///\n/// Returns `None` if the sender is not initialized (e.g., in Python/v1 bridge environments\n/// before a runner or adapter bridge has registered a sender).\n#[must_use]\npub fn try_get_data_event_sender() -> Option<tokio::sync::mpsc::UnboundedSender<DataEvent>> {\n    DATA_EVENT_SENDER.with(|sender| sender.borrow().as_ref().cloned())\n}\n\n/// Sets the thread-local data event sender.\n///\n/// Can only be called once per thread.\n///\n/// # Panics","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/live/runner.rs#L17-L53","documentation":"This panic comes from `get_data_event_sender()` in crates/common/src/live/runner.rs. The function reads a thread-local `DATA_EVENT_SENDER` slot that the live runner must initialize before any consumer fetches it; if the slot is still empty, the expect panics with this message. It exists so components on the live thread can obtain a channel sender for DataEvents without threading it through every constructor.","triggerScenarios":"Calling `get_data_event_sender()` on a thread where the live runner never ran its initialization (i.e. `DATA_EVENT_SENDER` was never set) — e.g. calling from a different thread than the runner's, calling before `run`/`connect` initializes it, or in unit tests without the runner harness.","commonSituations":"Spawning actor/strategy code on a fresh tokio task or OS thread separate from the runner thread and then grabbing the sender; constructing components in tests without using the runner's initialization path; early startup ordering where a component fetches the sender before the runner sets it.","solutions":["Ensure the live runner initializes the sender on the same thread before components call this function","Call this only from the runner's thread (it is thread-local); use `try_get_data_event_sender()` (the non-panicking variant) when unsure and handle None","Restructure initialization so senders are passed explicitly to components instead of fetched from thread-local state","In tests, initialize the thread-local sender via the runner harness before calling"],"exampleFix":"// before\nlet sender = get_data_event_sender(); // panics on uninitialized thread\n// after\nlet sender = match try_get_data_event_sender() {\n    Some(s) => s,\n    None => anyhow::bail!(\"data event sender not initialized; call from the live runner thread after initialization\"),\n};","handlingStrategy":"fallback","validationCode":"assert!(try_get_data_event_sender().is_some(), \"call from live runner thread after init\");","typeGuard":"fn sender_ready() -> bool { try_get_data_event_sender().is_some() }","tryCatchPattern":"// no exception to catch in Rust; use the non-panicking variant\nlet sender = try_get_data_event_sender().ok_or_else(|| anyhow!(\"data event sender not initialized\"))?;","preventionTips":["Call sender accessors only on the runner-initialized thread","Prefer try_get_* variants at library boundaries","Initialize runner channels before spawning components"],"tags":["rust","panic","thread-local","live-runner","initialization-order"],"backgroundTag":"missing-env-var","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}