nautechsystems/nautilus_trader · error
Client already running
Error message
Client already running
What it means
`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.
Source
Thrown at crates/adapters/databento/src/live.rs:260
}
/// Starts the live feed handler and returns its message receiver.
///
/// # Errors
///
/// Returns an error if the client is already closed, already running, or cannot start.
pub fn start(
&mut self,
) -> anyhow::Result<(
DatabentoFeedHandler,
tokio::sync::mpsc::UnboundedReceiver<DatabentoMessage>,
)> {
if self.is_closed {
anyhow::bail!("Client already closed");
}
if self.is_running {
anyhow::bail!("Client already running");
}
log::debug!("Starting client");
let (msg_tx, msg_rx) = tokio::sync::mpsc::unbounded_channel::<DatabentoMessage>();
let cmd_rx = self
.cmd_rx
.take()
.ok_or_else(|| anyhow::anyhow!("Command receiver already taken"))?;
let feed_handler = DatabentoFeedHandler::new(
self.credential.clone(),
self.dataset.clone(),
cmd_rx,
msg_tx,
self.publisher_venue_map.clone(),
self.symbol_venue_map.clone(),
self.use_exchange_as_venue,View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before
client.start().await?;
client.start().await?; // error: already running
// after
if !client.is_running() {
client.start().await?;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Idempotent start gate
let started = AtomicBool::new(false);
if !started.swap(true, Ordering::SeqCst) {
client.start().await?;
} Try / catch
// Treat 'already running' as success
match client.start().await {
Ok(pair) => pair,
Err(e) if e.to_string().contains("already running") => {
// obtain existing rx via a getter instead
unreachable_or_fetch_existing()
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: Calling `start` (or `py_start`) twice on the same client without an intervening `close()`.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Client already closed
- Client never started
- No feed handler found for dataset: {dataset}
- Command receiver already taken
- Active execution intent {intent_id} was not found
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/67657b3f57c74bf2.
Report an issue: GitHub.