{"record":{"id":"5d0529f37ad48815","repo":"nautechsystems/nautilus_trader","slug":"heartbeat-timeout-secs-must-cover-at-least-two-ser","errorCode":null,"errorMessage":"heartbeat_timeout_secs must cover at least two server heartbeat intervals ({DEAD_PEER_TIMEOUT_MIN_SECS}s), was {timeout_secs}s","messagePattern":"heartbeat_timeout_secs must cover at least two server heartbeat intervals \\((.+?)s\\), was (.+?)s","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/betfair/src/stream/config.rs","lineNumber":85,"sourceCode":"        self.heartbeat_timeout_secs\n            .unwrap_or(DEAD_PEER_TIMEOUT_MIN_SECS)\n    }\n\n    /// Validates heartbeat settings.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if an outbound interval is zero or an explicit dead-peer timeout is\n    /// shorter than two server intervals.\n    pub fn validate(&self) -> anyhow::Result<()> {\n        if self.heartbeat_secs == Some(0) {\n            anyhow::bail!(\"heartbeat_secs must be positive when set\");\n        }\n\n        if let Some(timeout_secs) = self.heartbeat_timeout_secs\n            && timeout_secs < DEAD_PEER_TIMEOUT_MIN_SECS\n        {\n            anyhow::bail!(\n                \"heartbeat_timeout_secs must cover at least two server heartbeat intervals \\\n                 ({DEAD_PEER_TIMEOUT_MIN_SECS}s), was {timeout_secs}s\",\n            );\n        }\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use rstest::rstest;\n\n    use super::*;\n\n    #[rstest]\n    fn test_stream_config_defaults() {\n        let config = BetfairStreamConfig::default();","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/betfair/src/stream/config.rs#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconfig.heartbeat_secs = Some(5);\nconfig.heartbeat_timeout_secs = Some(3); // below minimum, fails\n// after\nconfig.heartbeat_secs = Some(5);\nconfig.heartbeat_timeout_secs = Some(10); // covers two intervals","handlingStrategy":"validation","validationCode":"if let Some(t) = heartbeat_timeout_secs {\n    let min = 2 * heartbeat_secs.unwrap_or_default();\n    assert!(t >= min && t >= DEAD_PEER_TIMEOUT_MIN_SECS, \"timeout too short\");\n}","typeGuard":null,"tryCatchPattern":"match config.validate() {\n    Err(e) if e.to_string().contains(\"heartbeat_timeout_secs\") =>\n        eprintln!(\"raise heartbeat_timeout_secs to >= 2x heartbeat interval\"),\n    Err(e) => eprintln!(\"config invalid: {e}\"),\n    Ok(()) => {},\n}","preventionTips":["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."],"tags":["betfair","streaming","config","timeout","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}