nautechsystems/nautilus_trader · error

heartbeat_interval_secs must be greater than 0

Error message

heartbeat_interval_secs must be greater than 0

What it means

RedisMessageBus::new validates MessageBusConfig before constructing the bus. A heartbeat interval of Some(0) is rejected because a zero-second heartbeat period would spin the heartbeat task in a tight loop, so the constructor fails fast with this error instead.

Source

Thrown at crates/infrastructure/src/redis/msgbus.rs:285

    }
}

impl RedisMessageBusBacking {
    /// Creates a new [`RedisMessageBusBacking`] instance for the given `trader_id`, `instance_id`, and `config`.
    ///
    /// # Errors
    ///
    /// Returns an error if the heartbeat interval is configured as zero seconds.
    pub fn new(
        trader_id: TraderId,
        instance_id: UUID4,
        config: MessageBusConfig,
        backing: RedisMessageBusConfig,
    ) -> anyhow::Result<Self> {
        install_cryptographic_provider();

        if config.heartbeat_interval_secs == Some(0) {
            anyhow::bail!("heartbeat_interval_secs must be greater than 0");
        }

        let external_streams = config.external_streams.clone().unwrap_or_default();
        let heartbeat_interval_secs = config.heartbeat_interval_secs;
        let publish = backing.clone();

        let (pub_tx, pub_rx) = tokio::sync::mpsc::unbounded_channel::<BusMessage>();

        // Create publish task (start the runtime here for now)
        let pub_handle = Some(get_runtime().spawn(async move {
            if let Err(e) = publish_messages(pub_rx, trader_id, instance_id, config, publish).await
            {
                log_task_error(MSGBUS_PUBLISH, &e);
            }
        }));

        // Conditionally create stream task and channel if external streams configured
        let stream_signal = Arc::new(AtomicBool::new(false));

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set heartbeat_interval_secs to None if you want the heartbeat disabled
  2. Set a positive value, e.g. Some(30) for a 30-second heartbeat
  3. Clamp or validate the value where the config is loaded (env var / config file parsing) before constructing the bus
  4. Check the expression producing the interval — a zero-derived value usually signals an unset input

Example fix

// before
let config = MessageBusConfig { heartbeat_interval_secs: Some(0), .. };
// after
let config = MessageBusConfig { heartbeat_interval_secs: None, .. }; // or Some(30)
Defensive patterns

Strategy: validation

Validate before calling

assert!(config.heartbeat_interval_secs.map_or(true, |s| s > 0), "heartbeat_interval_secs must be > 0 or None");

Try / catch

let bus = RedisMessageBus::new(config, backing)
    .map_err(|e| if e.to_string().contains("heartbeat_interval_secs") { ConfigError::BadHeartbeat } else { e })?;

Prevention

When it happens

Trigger: Constructing the message bus with MessageBusConfig { heartbeat_interval_secs: Some(0), .. } — passing zero explicitly rather than None (disabled) or a positive number.

Common situations: Copy-pasting a config template and setting 0 to 'disable' the heartbeat; computing the interval from an expression that evaluates to 0 (e.g. seconds parsed from an empty/zero env var).

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/87e63e23d6ace7ce. Report an issue: GitHub.