nautechsystems/nautilus_trader · error
heartbeat_timeout_secs must cover at least two server heartb
Error message
heartbeat_timeout_secs must cover at least two server heartbeat intervals ({DEAD_PEER_TIMEOUT_MIN_SECS}s), was {timeout_secs}s What it means
The Betfair stream config requires that an explicitly configured dead-peer timeout (heartbeat_timeout_secs) be at least DEAD_PEER_TIMEOUT_MIN_SECS seconds — two server heartbeat intervals — so the client does not declare the peer dead before a second missed heartbeat could arrive. Shorter values fail validation in StreamConfig::validate.
Source
Thrown at crates/adapters/betfair/src/stream/config.rs:85
self.heartbeat_timeout_secs
.unwrap_or(DEAD_PEER_TIMEOUT_MIN_SECS)
}
/// Validates heartbeat settings.
///
/// # Errors
///
/// Returns an error if an outbound interval is zero or an explicit dead-peer timeout is
/// shorter than two server intervals.
pub fn validate(&self) -> anyhow::Result<()> {
if self.heartbeat_secs == Some(0) {
anyhow::bail!("heartbeat_secs must be positive when set");
}
if let Some(timeout_secs) = self.heartbeat_timeout_secs
&& timeout_secs < DEAD_PEER_TIMEOUT_MIN_SECS
{
anyhow::bail!(
"heartbeat_timeout_secs must cover at least two server heartbeat intervals \
({DEAD_PEER_TIMEOUT_MIN_SECS}s), was {timeout_secs}s",
);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_stream_config_defaults() {
let config = BetfairStreamConfig::default();View on GitHub (pinned to 18893faf8b)
Solutions
- Raise heartbeat_timeout_secs to at least DEAD_PEER_TIMEOUT_MIN_SECS (two server heartbeat intervals).
- Leave heartbeat_timeout_secs as None to use the adapter's default dead-peer policy.
- Reduce heartbeat_secs first if you need faster detection, then keep the timeout at >= 2x that interval.
Example fix
// before config.heartbeat_secs = Some(5); config.heartbeat_timeout_secs = Some(3); // below minimum, fails // after config.heartbeat_secs = Some(5); config.heartbeat_timeout_secs = Some(10); // covers two intervals
Defensive patterns
Strategy: validation
Validate before calling
if let Some(t) = heartbeat_timeout_secs {
let min = 2 * heartbeat_secs.unwrap_or_default();
assert!(t >= min && t >= DEAD_PEER_TIMEOUT_MIN_SECS, "timeout too short");
} Try / catch
match config.validate() {
Err(e) if e.to_string().contains("heartbeat_timeout_secs") =>
eprintln!("raise heartbeat_timeout_secs to >= 2x heartbeat interval"),
Err(e) => eprintln!("config invalid: {e}"),
Ok(()) => {},
} Prevention
- Always set timeout >= 2 * heartbeat interval; never tune the timeout below the constant minimum.
- To detect dead peers faster, lower heartbeat_secs first and keep the 2x ratio.
- Document the constraint next to wherever the config values are authored.
When it happens
Trigger: Setting heartbeat_timeout_secs to a value smaller than DEAD_PEER_TIMEOUT_MIN_SECS (e.g. 5s when the minimum is 10s for a 5s heartbeat) and calling connect via connect_inner -> validate.
Common situations: Users trying to 'fail fast' on dead connections by lowering the timeout below the safe minimum; copying a timeout tuned for another exchange's faster heartbeats; guessing the constraint because it is only stated in the validate doc comment.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- heartbeat_secs must be positive when set
- request_rate_per_second must be greater than zero
- order_request_rate_per_second must be greater than zero
- Timeout must be less than 3600 seconds
- Invalid account currency code: {code}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5d0529f37ad48815.
Report an issue: GitHub.