nautechsystems/nautilus_trader · error

Either replay `options` or `stream_options` must be provided

Error message

Either replay `options` or `stream_options` must be provided

What it means

The Tardis data client's connect() requires the config to specify what data to consume: historical replay `options` or realtime `stream_options`. If both lists are empty there is nothing to subscribe to, so the client refuses to connect rather than opening a useless websocket session.

Source

Thrown at crates/adapters/tardis/src/data.rs:511

    fn unsubscribe_mark_prices(&mut self, _cmd: &UnsubscribeMarkPrices) -> anyhow::Result<()> {
        Ok(())
    }

    fn unsubscribe_index_prices(&mut self, _cmd: &UnsubscribeIndexPrices) -> anyhow::Result<()> {
        Ok(())
    }

    fn unsubscribe_funding_rates(&mut self, _cmd: &UnsubscribeFundingRates) -> anyhow::Result<()> {
        Ok(())
    }

    async fn connect(&mut self) -> anyhow::Result<()> {
        if self.is_connected() && self.tasks.is_open() {
            return Ok(());
        }

        if self.config.options.is_empty() && self.config.stream_options.is_empty() {
            anyhow::bail!("Either replay `options` or `stream_options` must be provided");
        }

        if !self.tasks.is_open() {
            self.tasks
                .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2))
                .await
                .map_err(|e| anyhow::anyhow!("Failed to terminate Tardis tasks: {e}"))?;
            self.tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Tardis task generation: {e}"))?;
            self.cancellation_token = self.tasks.cancellation_token();
        }

        let is_stream_mode = self.is_stream_mode();
        let book_snapshot_output = self.config.book_snapshot_output.clone();
        let extract_bbo_as_quotes = self.config.extract_bbo_as_quotes;

        let http_client = TardisHttpClient::new(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Populate `options` with TardisReplayOptions for historical data, e.g. TardisReplayOptions::new(exchange, "trades", symbols, None)
  2. Populate `stream_options` with TardisStreamOptions for realtime feeds
  3. Fix the config-building logic that produced an empty options list
  4. Validate the config before constructing the client

Example fix

// before
let config = TardisDataClientConfig::default();  // both options empty
// after
let config = TardisDataClientConfig {
    options: vec![TardisReplayOptions::new("binance-futures", "trades", Some(vec!["btcusdt".into()]), None)],
    stream_options: vec![],
    ..Default::default()
};
Defensive patterns

Strategy: validation

Validate before calling

fn validate_tardis_config(config: &TardisDataClientConfig) -> Result<(), String> {
    if config.options.is_empty() && config.stream_options.is_empty() {
        return Err("Provide at least one replay `options` or `stream_options` entry".into());
    }
    Ok(())
}

Try / catch

match client.connect().await {
    Ok(()) => {},
    Err(e) if e.to_string().contains("Either replay `options`") => {
        eprintln!("Tardis config has no subscriptions; populate options/stream_options");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Creating a TardisDataClient (or factory) with a TardisDataClientConfig where both `options` and `stream_options` are empty/default, then calling connect.

Common situations: Config built programmatically with all fields defaulted; options filtered out by conditional logic that produced an empty vec; user expected a default subscription set that doesn't exist.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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