nautechsystems/nautilus_trader · error · anyhow::Error
Failed to send message: {e}
Error message
Failed to send message: {e} What it means
The WebSocket handler could not deliver a text message (subscribe/unsubscribe control frame) through the active Binance Spot stream connection. `send_text` on the inner client failed with the underlying error `{e}` (connection closed, rate limiter rejection, backpressure, etc.).
Source
Thrown at crates/adapters/binance/src/spot/websocket/streams/handler.rs:297
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![];
};
if value.get("e").and_then(|v| v.as_str()) == Some("serverShutdown")
&& let Ok(msg) = serde_json::from_value::<BinanceSpotServerShutdownMsg>(value.clone())
{
log::warn!(
"Binance server shutdown notice received (event_time={}); disconnect expected ~10 minutes from event",View on GitHub (pinned to 18893faf8b)
Solutions
- Check the underlying `{e}`: for a closed connection, reconnect the stream client and re-issue the subscription.
- Verify rate-limit configuration (`rate_limit_keys`) — reduce subscription churn or increase limits if the send was throttled.
- Ensure the WebSocket connection is established and healthy before calling subscribe/unsubscribe.
- Add retry/backoff around send for transient errors and monitor the stream's reconnect lifecycle.
Example fix
// before: fire-and-forget subscribe, error aborts handler
client.send_text(payload, keys).await?;
// after: tolerate transient failure, allow reconnect logic to resubscribe
match client.send_text(payload, keys).await {
Ok(()) => Ok(()),
Err(e) => { warn!("send failed, will resubscribe after reconnect: {e}"); Ok(()) }
} Defensive patterns
Strategy: retry
Validate before calling
if stream_client.connection_state() != ConnectionState::Active {
return Err(anyhow::anyhow!("cannot send: websocket not active"));
} Try / catch
match client.send_text(payload, keys).await {
Ok(()) => Ok(()),
Err(e) if is_connection_closed(&e) => {
stream_client.reconnect().await?;
client.send_text(payload, keys).await
.map_err(|e| anyhow::anyhow!("Failed to send message: {e}"))
}
Err(e) => Err(anyhow::anyhow!("Failed to send message: {e}")),
} Prevention
- Track connection lifecycle and re-issue subscriptions after every reconnect.
- Add exponential backoff on send failures.
- Keep subscription traffic within Binance message rate limits.
When it happens
Trigger: `handle_subscribe` or `handle_unsubscribe` builds a subscription payload and calls `send_text` while the connection is being closed or saturated; the inner `client.send_text(payload, rate_limit_keys).await` returns `Err(e)` which is wrapped into this error.
Common situations: WebSocket dropped mid-session (network blip, server shutdown) and a subscribe is still pending; exceeding Binance message rate limits; trying to send before the connection handshake finished despite `inner` being set.
Related errors
- Binance Spot data teardown failed: {}
- Binance Futures data teardown failed: {}
- WS modify order failed: {e}
- WS submit order failed: {e}
- WS cancel order failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/77a7cf9cfc501a6d.
Report an issue: GitHub.