{"record":{"id":"4671929fbb0a67c2","repo":"rustdesk/rustdesk-server","slug":"timeout","errorCode":null,"errorMessage":"Timeout","messagePattern":"Timeout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/relay_server.rs","lineNumber":564,"sourceCode":"                    let nb = bytes.len() * 8;\n                    if blacked || downgrade {\n                        blacklist_limiter.consume(nb).await;\n                    } else {\n                        limiter.consume(nb).await;\n                    }\n                    total_limiter.consume(nb).await;\n                    total += nb;\n                    total_s += nb;\n                    if !bytes.is_empty() {\n                        peer.send_raw(bytes.into()).await?;\n                    }\n                } else {\n                    break;\n                }\n            },\n            _ = timer.tick() => {\n                if last_recv_time.elapsed().as_secs() > 30 {\n                    bail!(\"Timeout\");\n                }\n            }\n        }\n\n        let n = tm.elapsed().as_millis() as usize;\n        if n >= 1_000 {\n            if BLOCKLIST.read().await.get(&ip).is_some() {\n                log::info!(\"{} blocked\", ip);\n                break;\n            }\n            blacked = BLACKLIST.read().await.get(&ip).is_some();\n            tm = std::time::Instant::now();\n            let speed = total_s / n;\n            if speed > highest_s {\n                highest_s = speed;\n            }\n            elapsed += n;\n            USAGE.write().await.insert(","sourceCodeStart":546,"sourceCodeEnd":582,"githubUrl":"https://github.com/rustdesk/rustdesk-server/blob/a7736be5e40f85bfc141120dce587e836e5d4b80/src/relay_server.rs#L546-L582","documentation":"In the relay server's pairing/forwarding loop, a tokio interval timer checks that data was received within the last 30 seconds; if `last_recv_time` is older, the relay connection is torn down with a bail!(\"Timeout\"). This prevents relayed connections from holding sockets and the pairing slot open forever when a peer stops sending. The raised task is `relay`, invoked from `make_pair_`.","triggerScenarios":"A relayed client or its peer stops sending any bytes for >30 seconds while the relay select loop is running (timer.tick() branch fires and elapsed().as_secs() > 30).","commonSituations":"Client behind aggressive NAT/firewall dropping idle TCP; remote peer crashed or network outage mid-session; mobile client suspending and killing its socket; very long idle relay sessions with no keepalive.","solutions":["Ensure clients send periodic keepalive/heartbeat data over the relay connection so last_recv_time is refreshed.","Check network path (NAT timeouts, firewall idle-connection drops) between client and relay; enable TCP keepalive.","Retry the relay connection from the client after the timeout; the relay is expected to be restarted/re-paired.","If 30s is too aggressive for your deployment, raise the threshold in src/relay_server.rs and recompile."],"exampleFix":"// client side: send a heartbeat periodically\nlet mut ka = tokio::time::interval(Duration::from_secs(10));\ntokio::select! {\n    _ = ka.tick() => stream.send(&keepalive_msg).await?,\n    n = stream.recv() => { /* refresh last_recv_time on relay */ }\n}","handlingStrategy":"retry","validationCode":"// before connecting, confirm NAT/firewall allows the relay port and long-lived TCP\nnc -zvw5 <relay-host> 21117","typeGuard":null,"tryCatchPattern":"match relay_connect().await {\n    Err(e) if e.to_string().contains(\"Timeout\") => schedule_reconnect_with_backoff(),\n    Err(e) => log::error!(\"relay failed: {e}\"),\n    Ok(s) => pump_with_keepalive(s).await,\n}","preventionTips":["Send application-level heartbeats at intervals well under 30s.","Enable TCP keepalive on relay sockets.","Raise NAT/firewall idle timeouts for the relay port.","Implement automatic re-pair on the client when the relay drops."],"tags":["network","timeout","relay","idle-connection"],"backgroundTag":"request-timeout","analyzedSha":"a7736be5e40f85bfc141120dce587e836e5d4b80","analyzedAt":"2026-09-09T21:56:29.933Z","contentChangedAt":"2026-09-09T21:56:29.933Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}