nautechsystems/nautilus_trader · error
No active WebSocket client
Error message
No active WebSocket client
What it means
The streams WebSocket handler's send_text requires an active inner WS client; when self.inner is None the handler has no established connection to send on, so it bails. Messages can only be sent after a successful connect that populates inner.
Source
Thrown at crates/adapters/binance/src/spot/websocket/streams/handler.rs:292
payload,
Some(BINANCE_RATE_LIMIT_KEY_SUBSCRIPTION.as_slice()),
)
.await
{
self.pending_requests.take(request_id);
return Err(e);
}
Ok(())
}
async fn send_text(
&self,
payload: String,
rate_limit_keys: Option<&[Ustr]>,
) -> anyhow::Result<()> {
let Some(client) = &self.inner else {
anyhow::bail!("No active WebSocket client");
};
client
.send_text(payload, rate_limit_keys)
.await
.map_err(|e| anyhow::anyhow!("Failed to send message: {e}"))?;
Ok(())
}
}
/// Classifies a JSON text frame that did not match a subscription response or
/// known error envelope. Recognizes the `serverShutdown` event; otherwise
/// emits `RawJson` for parseable payloads or an empty vector for garbage.
fn classify_unsolicited_json(text: &str) -> Vec<BinanceSpotWsMessage> {
let Ok(value) = serde_json::from_str::<serde_json::Value>(text) else {
log::warn!("Failed to parse JSON message: {text}");
return vec![];
};
View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure connect() is awaited and succeeded before any subscribe/unsubscribe commands.
- Buffer or re-queue subscription commands during reconnection and replay once the client is active.
- Check logs for the disconnect that cleared inner; if unexpected, investigate connection stability.
- Guard the caller with a connected-state check before sending commands.
Example fix
// before
handler.send_text(payload, keys).await?;
// after
if !handler.is_connected().await {
log::warn!("not connected, queuing subscribe command");
pending_commands.push((payload, keys));
} else {
handler.send_text(payload, keys).await?;
} Defensive patterns
Strategy: type-guard
Type guard
async fn is_connected(handler: &Handler) -> bool { handler.inner.is_some() } Try / catch
match handler.send_text(payload, keys).await {
Err(e) if e.to_string().contains("No active WebSocket client") => {
queue_for_replay((payload, keys)); // resend after reconnect
}
other => other?,
} Prevention
- Always await connect() successfully before issuing subscribe/unsubscribe commands.
- Queue commands during reconnection windows and replay them once the client is active.
- Track connection state in the caller and gate all sends on it.
- Investigate unexpected disconnects that clear the inner client.
When it happens
Trigger: handle_subscribe or handle_unsubscribe calling send_text before connect() completed, or after the inner client was cleared by disconnect/reconnect, or when a connection was never established because connect failed.
Common situations: Subscribing immediately after constructing the handler without connecting; a dropped connection cleared inner while subscription management still runs; reconnection windows where commands arrive between disconnect and reconnect.
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
- Binance Spot public JSON stream pool is shutting down
- WS submit order failed: {e}
- WS cancel order failed: {e}
- WS modify order failed: {e}
- WS setup error channel closed
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f8f10cfe52f3c09f.
Report an issue: GitHub.