rustdesk/rustdesk-server · warning · std::io::Error
err.to_string()
Error message
err.to_string()
What it means
In the relay server's WebSocket-to-stream adapter, when the underlying websocket read fails, the error is converted into an std::io::Error of kind Other carrying err.to_string() as its message. Downstream code surfaces this io error (message = the tungstenite error text) when the websocket connection with the peer breaks.
Solutions
- Treat this as an expected disconnect on the client side: reconnect and re-pair the relay session with retry/backoff.
- Inspect the wrapped tungstenite message (it is preserved verbatim in the io::Error) to identify close/reset vs protocol error.
- Enable websocket ping/pong or lower idle timeouts on proxies so intermediaries do not silently kill connections.
- Check TLS certificates and websocket upgrade support if errors cluster on wss:// endpoints.
Example fix
// client side: resilient reconnect
loop {
match connect_and_relay().await {
Ok(()) => break,
Err(e) if e.kind() == std::io::ErrorKind::Other => {
tokio::time::sleep(backoff).await;
backoff = (backoff * 2).min(MAX_BACKOFF);
}
Err(e) => return Err(e),
}
} Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: confirm websocket endpoint is reachable and upgrades correctly let (ws, _) = tokio_tungstenite::connect_async(url).await?; // fails fast before relay starts
Try / catch
match session.next().await {
Ok(Ok(_msg)) => { /* forward bytes */ }
Ok(Err(e)) => reconnect_with_backoff(Error::new(std::io::ErrorKind::Other, e.to_string())),
Err(e) => reconnect_with_backoff(e.into()),
} Prevention
- Use websocket ping/pong frames to keep intermediaries from killing idle connections.
- Configure proxies/LBs with websocket-friendly idle timeouts.
- Implement client-side reconnect with exponential backoff around relay sessions.
- Log the wrapped tungstenite error text to distinguish closes from protocol errors.
When it happens
Trigger: The tungstenite websocket read returns Err - connection closed by peer, TCP reset, protocol violation, or TLS failure - while bridging the websocket into the relay byte stream.
Common situations: Client disconnects abruptly mid-relay (network drop, app kill); proxy/load-balancer terminating idle websocket connections; tungstenite protocol/version mismatch; TLS certificate problems on wss endpoints.
Related errors
AI-assisted analysis of rustdesk/rustdesk-server@a7736be5e4 (2026-09-09).
Data as JSON: /api/errors/465a93e3b1863671.
Report an issue: GitHub.
Appendix: source
Thrown at src/relay_server.rs:665
fn set_raw(&mut self) {
self.set_raw();
}
}
#[async_trait]
impl StreamTrait for tokio_tungstenite::WebSocketStream<TcpStream> {
async fn recv(&mut self) -> Option<Result<BytesMut, Error>> {
if let Some(msg) = self.next().await {
match msg {
Ok(msg) => {
match msg {
tungstenite::Message::Binary(bytes) => {
Some(Ok(bytes[..].into())) // to-do: poor performance
}
_ => Some(Ok(BytesMut::new())),
}
}
Err(err) => Some(Err(Error::new(std::io::ErrorKind::Other, err.to_string()))),
}
} else {
None
}
}
async fn send_raw(&mut self, bytes: Bytes) -> ResultType<()> {
Ok(self
.send(tungstenite::Message::Binary(bytes.to_vec()))
.await?) // to-do: poor performance
}
fn is_ws(&self) -> bool {
true
}
fn set_raw(&mut self) {}
}View on GitHub (pinned to a7736be5e4)