nautechsystems/nautilus_trader · error
Cannot take ownership of stream - client was cloned and othe
Error message
Cannot take ownership of stream - client was cloned and other references exist
What it means
In AxOrdersWebSocketClient::stream(), the shared receiver is an Arc and stream() requires unique ownership (Arc::try_unwrap) to return a 'static stream. If clones of the client still exist, try_unwrap returns Err and this expect panics, protecting the single-consumer contract of the orders stream.
Source
Thrown at crates/adapters/architect_ax/src/websocket/orders/client.rs:703
self.send_cmd(HandlerCommand::GetOpenOrders { request_id })
.await?;
Ok(request_id)
}
/// Returns a stream of WebSocket messages.
///
/// # Panics
///
/// Panics if called before `connect()` or if the stream has already been taken.
pub fn stream(&mut self) -> impl futures_util::Stream<Item = AxOrdersWsMessage> + 'static {
let rx = self
.out_rx
.take()
.expect("Stream receiver already taken or client not connected - stream() can only be called once");
let mut rx = Arc::try_unwrap(rx).expect(
"Cannot take ownership of stream - client was cloned and other references exist",
);
async_stream::stream! {
while let Some(msg) = rx.recv().await {
yield msg;
}
}
}
pub(crate) fn begin_shutdown(&self) {
self.cancellation_token.load().cancel();
self.signal.store(true, Ordering::Release);
}
/// Disconnects the WebSocket connection gracefully.
pub async fn disconnect(&self) {
log::debug!("Disconnecting WebSocket");
let _ = self.send_cmd(HandlerCommand::Disconnect).await;
}View on GitHub (pinned to 18893faf8b)
Solutions
- Drop all clones before calling stream(), or call stream() only on the final remaining handle.
- Dedicate one handle exclusively to streaming and use clones only for send operations.
- Fan out messages yourself: consume the stream in one task and forward to per-consumer channels.
- If the design truly needs concurrent receivers, switch to a broadcast channel in your wrapper layer.
Example fix
// before let clone = client.clone(); send_orders(clone); // clone moved into task but another clone retained let stream = client.stream(); // panics: references exist // after // keep exactly one handle for streaming let stream = client.stream(); // derive senders from a separate connection or drop clones first
Defensive patterns
Strategy: validation
Validate before calling
// Ensure no clones of the orders client are alive before streaming // (avoid .clone() on the client intended for stream(); keep one unique handle)
Prevention
- Reserve one client handle exclusively for streaming; use separate clients or channels for senders.
- Audit code paths that clone the client into tasks or shared state.
- If concurrent receivers are needed, fan out from the single stream via channels.
When it happens
Trigger: Calling stream() while any clone of the AxOrdersWebSocketClient (created with .clone()) is still alive, e.g. a clone held by an order-sending task or a long-lived actor.
Common situations: Sharing one client between an order manager and a WebSocket event loop; holding a clone in application state while the stream is consumed elsewhere; tests cloning the client to inspect state before streaming.
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
- Cannot take ownership of stream - client was cloned and othe
- Stream receiver already taken or client not connected - stre
- Stream receiver already taken or client not connected - stre
- Cannot take ownership - other references exist
- InstrumentState channel requires kind and currency parameter
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/454864bc4984a297.
Report an issue: GitHub.