nautechsystems/nautilus_trader · error · anyhow::Error
Command receiver already taken
Error message
Command receiver already taken
What it means
`DatabentoLiveClient::start` takes the command receiver (`cmd_rx`) out of an `Option` that is consumed on first start; calling `start` a second time finds `None` and raises this error. It enforces the one-shot lifecycle of the client's internal command channel — a client instance cannot be started twice.
Source
Thrown at crates/adapters/databento/src/live.rs:269
) -> 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,
self.bars_timestamp_on_close,
self.reconnect_timeout_mins,
);
self.send_command(HandlerCommand::Start)?;
self.is_running = true;
Ok((feed_handler, msg_rx))
}View on GitHub (pinned to 18893faf8b)
Solutions
- Create a new `DatabentoLiveClient` instance instead of restarting the consumed one
- Track client state and only call `start` once per instance
- On failure/reconnect, replace the client (rebuild via constructor) rather than re-calling start
- Check whether a previous `start`/`stop` already consumed the receiver before calling start
Example fix
// before
if client_failed {
client.start().await?; // panics with "Command receiver already taken"
}
// after
if client_failed {
client = DatabentoLiveClient::new(/* same config */)?;
client.start().await?;
} Defensive patterns
Strategy: type-guard
Validate before calling
// before start
if client.is_running() {
anyhow::bail!("client already started");
} Type guard
fn can_start(client: &DatabentoLiveClient) -> bool {
!client.is_started() // track a started flag in your wrapper
} Try / catch
match client.start().await {
Ok(()) => {},
Err(e) if e.to_string().contains("Command receiver already taken") => {
log::warn!("client already started; rebuilding instance");
client = DatabentoLiveClient::new(/* config */)?;
client.start().await?;
}
Err(e) => return Err(e),
} Prevention
- Treat client instances as single-use: start once, rebuild for restarts
- Never retry failures by re-calling start on the same instance
- Encapsulate lifecycle in a supervisor that owns construction and start
When it happens
Trigger: Calling `start` (or `py_start`) twice on the same live client instance; restarting a client after a session ended without constructing a fresh client; calling start after `stop` or after a previous run consumed the receiver.
Common situations: Restart logic in a long-running process reusing the old client object; exception handlers that retry by calling `start` again instead of rebuilding the client; Python wrappers inadvertently invoking start twice (e.g. both framework and user code).
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
- Cannot add components in current state: {}
- Cannot add execution algorithms in current state: {}
- No symbols provided
- Reconnection timeout after {timeout_mins} minutes: {e}
- on_symbol_mapping failed for {msg:?}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/65e5ece1e6a234ba.
Report an issue: GitHub.