nautechsystems/nautilus_trader · error
heartbeat_secs must be positive when set
Error message
heartbeat_secs must be positive when set
What it means
The Betfair streaming config validator rejects a heartbeat interval of exactly 0 seconds. A zero heartbeat is meaningless (it would mean 'ping continuously'), so configuration with Some(0) fails validation before the stream connects. This guard runs inside StreamConfig::validate, called by connect_inner.
Source
Thrown at crates/adapters/betfair/src/stream/config.rs:79
}
}
impl BetfairStreamConfig {
#[must_use]
pub fn dead_peer_timeout_secs(&self) -> u64 {
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;View on GitHub (pinned to 18893faf8b)
Solutions
- Set heartbeat_secs to a positive value, e.g. Some(10) matching Betfair's recommended heartbeat (typically 5-30 seconds).
- If you want no explicit heartbeat, leave the field as None instead of Some(0).
- Fix the source of the value (env var, config file, CLI default) so it never produces 0.
Example fix
// before let mut config = StreamConfig::default(); config.heartbeat_secs = Some(0); // fails validation // after config.heartbeat_secs = Some(10); // or leave as None
Defensive patterns
Strategy: validation
Validate before calling
if let Some(hb) = heartbeat_secs {
assert!(hb > 0, "heartbeat_secs must be positive when set");
} Try / catch
let config = StreamConfig { heartbeat_secs, .. };
if let Err(e) = config.validate() {
return Err(format!("invalid stream config: {e}"));
} Prevention
- Use None, not Some(0), to mean 'no explicit heartbeat'.
- Clamp parsed env/config integers to >= 1 before constructing the config.
- Call validate() at config-load time, not just at connect time, for early failure.
When it happens
Trigger: Constructing a Betfair stream config with heartbeat_secs = Some(0) and then calling connect (via connect_inner -> validate).
Common situations: Setting the value from an environment variable parsed to 0 by default; a user typing heartbeat=0 intending 'disable heartbeat' instead of leaving it None; templated config files filled with placeholder zeros.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- heartbeat_timeout_secs must cover at least two server heartb
- request_rate_per_second must be greater than zero
- order_request_rate_per_second must be greater than zero
- missing persistence type for order update {}
- signature_expiry_secs {signature_expiry_secs}s must be great
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5d33549d2aab1608.
Report an issue: GitHub.