rustdesk/rustdesk-server · error
Timeout of test_hbbs
Error message
Timeout of test_hbbs
What it means
This is a self-test watchdog inside hbbs: when the server runs in test mode (test_hbbs, started by start_with_bind), it waits for a parsed RendezvousMessage on its socket; if nothing arrives for more than 12 seconds, it bails with "Timeout of test_hbbs", ending the self-test. It exists so a broken server loop fails fast instead of hanging forever.
Solutions
- Ensure the test client sends a valid, current-protocol RendezvousMessage promptly (well under 12s) after the server starts.
- Check loopback networking/firewall rules so the test packet can reach the bound socket.
- Enable trace logging (log::trace shows 'Recv ... of test_hbbs') to see whether messages arrive but fail to parse.
- If the test legitimately needs longer, increase the 12-second threshold in src/rendezvous_server.rs.
Example fix
// test harness: send the probe message immediately, with retry
let mut attempts = 0;
while attempts < 3 {
socket.send(&msg, addr).await.ok();
if socket.recv_timeout(Duration::from_secs(2)).is_ok() { break; }
attempts += 1;
} Defensive patterns
Strategy: try-catch
Validate before calling
// CI: verify the probe message parses with the same protocol version before starting the test
let _ = RendezvousMessage::parse_from_bytes(&probe_bytes).expect("probe must parse"); Try / catch
// wrap the self-test runner with a timeout-tolerant wrapper
match tokio::time::timeout(Duration::from_secs(20), test_hbbs()).await {
Err(_) => log::warn!("test_hbbs watchdog fired (12s no-reply)"),
Ok(Err(e)) => log::error!("test_hbbs failed: {e}"),
Ok(Ok(())) => log::info!("test_hbbs passed"),
} Prevention
- Keep the test client and hbbs on the same protocol version so parse_from_bytes succeeds.
- Start the test client before or immediately with the server to stay under the 12s window.
- Allow loopback traffic in CI containers/firewalls.
- Raise the 12s threshold if CI machines are slow.
When it happens
Trigger: Running hbbs in test_hbbs mode and the local socket receives no parseable RendezvousMessage within 12 seconds - either the test client never sent a message, the packet was corrupt (parse_from_bytes failed), or the server loop is stuck.
Common situations: CI smoke tests of hbbs where the test client is slow to start or sends a malformed message; firewall/loopback issues preventing the test packet from arriving; protocol changes between test client and server breaking parse_from_bytes.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of rustdesk/rustdesk-server@a7736be5e4 (2026-09-09).
Data as JSON: /api/errors/8f2f20fcf43cd181.
Report an issue: GitHub.
Appendix: source
Thrown at src/rendezvous_server.rs:1356
} else {
IpAddr::V6(Ipv6Addr::LOCALHOST)
});
}
let mut socket = FramedSocket::new(config::Config::get_any_listen_addr(addr.is_ipv4())).await?;
let mut msg_out = RendezvousMessage::new();
msg_out.set_register_peer(RegisterPeer {
id: "(:test_hbbs:)".to_owned(),
..Default::default()
});
let mut last_time_recv = Instant::now();
let mut timer = interval(Duration::from_secs(1));
loop {
tokio::select! {
_ = timer.tick() => {
if last_time_recv.elapsed().as_secs() > 12 {
bail!("Timeout of test_hbbs");
}
socket.send(&msg_out, addr).await?;
}
Some(Ok((bytes, _))) = socket.next() => {
if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
log::trace!("Recv {:?} of test_hbbs", msg_in);
last_time_recv = Instant::now();
}
}
}
}
}
#[inline]
async fn send_rk_res(
socket: &mut FramedSocket,
addr: SocketAddr,
res: register_pk_response::Result,View on GitHub (pinned to a7736be5e4)