nautechsystems/nautilus_trader · error
heartbeat health margin should be shorter than the safety ti
Error message
heartbeat health margin should be shorter than the safety timeout
What it means
The Polymarket heartbeat task computes its health-check timeout as HEARTBEAT_SAFETY_TIMEOUT - HEARTBEAT_HEALTH_MARGIN using checked_sub on Durations. The expect asserts the compile-time constant margin is smaller than the safety timeout; with any valid build this cannot fail, so the panic indicates corrupted constants or a binary/patch tampering with them.
Source
Thrown at crates/adapters/polymarket/src/execution/lifecycle.rs:737
self.teardown_partial_connect().await?;
log::info!("Disconnected: client_id={}", self.core.client_id);
Ok(())
}
pub(super) fn on_instrument_update(&self, instrument: &InstrumentAny) {
self.upsert_execution_lookup(instrument);
}
}
async fn run_heartbeats(
http_client: crate::http::clob::PolymarketClobHttpClient,
cancellation: CancellationToken,
healthy: Arc<AtomicBool>,
) {
let heartbeat_health_timeout = HEARTBEAT_SAFETY_TIMEOUT
.checked_sub(HEARTBEAT_HEALTH_MARGIN)
.expect("heartbeat health margin should be shorter than the safety timeout");
let mut interval = tokio::time::interval(HEARTBEAT_INTERVAL);
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let mut heartbeat_id = String::new();
let mut request_failures = 0;
let mut last_acknowledged = None;
loop {
tokio::select! {
() = cancellation.cancelled() => break,
_ = interval.tick() => {}
}
let mut resynchronized = false;
loop {
let now = tokio::time::Instant::now();
let request_timeout = now
.checked_add(HEARTBEAT_REQUEST_TIMEOUT)View on GitHub (pinned to 18893faf8b)
Solutions
- Restore the stock constant relationship: HEARTBEAT_HEALTH_MARGIN must be strictly less than HEARTBEAT_SAFETY_TIMEOUT.
- Rebuild from an unmodified nautilus source tree.
- If tuning constants, add/keep a static assert or startup check validating margin < safety timeout.
Example fix
// before const HEARTBEAT_HEALTH_MARGIN: Duration = Duration::from_secs(20); const HEARTBEAT_SAFETY_TIMEOUT: Duration = Duration::from_secs(15); // margin > safety // after const HEARTBEAT_SAFETY_TIMEOUT: Duration = Duration::from_secs(30); const HEARTBEAT_HEALTH_MARGIN: Duration = Duration::from_secs(10); // margin < safety
Defensive patterns
Strategy: validation
Validate before calling
// build-time/static check if you tune the constants const _: () = assert!(HEARTBEAT_HEALTH_MARGIN < HEARTBEAT_SAFETY_TIMEOUT);
Prevention
- Keep stock timing constants; if tuning, always preserve margin < safety timeout.
- Add a compile-time assert to catch bad constant edits.
- Rebuild adapters from unmodified upstream sources.
When it happens
Trigger: Starting the heartbeat task (start_heartbeat_task) when HEARTBEAT_HEALTH_MARGIN >= HEARTBEAT_SAFETY_TIMEOUT — only possible if the constants were modified (e.g. local patch or a mis-scoped fork build).
Common situations: Forked adapter builds where timing constants were tweaked without preserving the invariant margin < safety timeout; binary-level constant corruption.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- heartbeat request timeout should fit in an instant
- Unsupported blockchain {blockchain} for RPC connection
- Kraken Spot does not support the demo environment
- Redis config error: username supplied without password. Eith
- Invalid HyperSync URL
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e14515ea1d4b1826.
Report an issue: GitHub.