{"record":{"id":"67657b3f57c74bf2","repo":"nautechsystems/nautilus_trader","slug":"client-already-running","errorCode":null,"errorMessage":"Client already running","messagePattern":"Client already running","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/databento/src/live.rs","lineNumber":260,"sourceCode":"    }\n\n    /// Starts the live feed handler and returns its message receiver.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the client is already closed, already running, or cannot start.\n    pub fn start(\n        &mut self,\n    ) -> anyhow::Result<(\n        DatabentoFeedHandler,\n        tokio::sync::mpsc::UnboundedReceiver<DatabentoMessage>,\n    )> {\n        if self.is_closed {\n            anyhow::bail!(\"Client already closed\");\n        }\n\n        if self.is_running {\n            anyhow::bail!(\"Client already running\");\n        }\n\n        log::debug!(\"Starting client\");\n\n        let (msg_tx, msg_rx) = tokio::sync::mpsc::unbounded_channel::<DatabentoMessage>();\n        let cmd_rx = self\n            .cmd_rx\n            .take()\n            .ok_or_else(|| anyhow::anyhow!(\"Command receiver already taken\"))?;\n\n        let feed_handler = DatabentoFeedHandler::new(\n            self.credential.clone(),\n            self.dataset.clone(),\n            cmd_rx,\n            msg_tx,\n            self.publisher_venue_map.clone(),\n            self.symbol_venue_map.clone(),\n            self.use_exchange_as_venue,","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/databento/src/live.rs#L242-L278","documentation":"`start` checks `is_running` before spinning up the feed handler and message channel. Calling start on an already-running client is refused with this error so the client never runs two feed handlers or leaks duplicate channels.","triggerScenarios":"Calling `start` (or `py_start`) twice on the same client without an intervening `close()`.","commonSituations":"Concurrent tasks racing to start the same client; retry logic re-invoking start after a timeout; startup code accidentally duplicated in strategy and kernel layers.","solutions":["Call `start` only once per client lifecycle; check an `is_running` flag or state getter before calling.","Synchronize startup behind a lock/async gate so only one caller starts the client.","If a fresh start is truly needed, call `close()` first, then construct a new client.","Wrap start in idempotent helper that returns the existing channel if already running."],"exampleFix":"// before\nclient.start().await?;\nclient.start().await?; // error: already running\n// after\nif !client.is_running() {\n    client.start().await?;\n}","handlingStrategy":"try-catch","validationCode":"// Idempotent start gate\nlet started = AtomicBool::new(false);\nif !started.swap(true, Ordering::SeqCst) {\n    client.start().await?;\n}","typeGuard":null,"tryCatchPattern":"// Treat 'already running' as success\nmatch client.start().await {\n    Ok(pair) => pair,\n    Err(e) if e.to_string().contains(\"already running\") => {\n        // obtain existing rx via a getter instead\n        unreachable_or_fetch_existing()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Serialize start calls behind a mutex/async lock","Make startup idempotent at the orchestration layer","Never call start from both a retry loop and the main path"],"tags":["databento","lifecycle","double-start","live-client"],"backgroundTag":"invalid-state-transition","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"}