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

  1. Treat this as an expected disconnect on the client side: reconnect and re-pair the relay session with retry/backoff.
  2. Inspect the wrapped tungstenite message (it is preserved verbatim in the io::Error) to identify close/reset vs protocol error.
  3. Enable websocket ping/pong or lower idle timeouts on proxies so intermediaries do not silently kill connections.
  4. 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

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)