nautechsystems/nautilus_trader · error

Venue {venue} is already registered

Error message

Venue {venue} is already registered

What it means

Each venue in a backtest must be unique: add_venue registers a SimulatedVenue under its Venue key in the engine's venue map. Registering the same venue twice is ambiguous (which exchange would clients route to?), so the engine rejects it eagerly.

Source

Thrown at crates/backtest/src/engine.rs:279

        self.backtest_end
    }

    /// Returns the list of registered venue identifiers.
    #[must_use]
    pub fn list_venues(&self) -> Vec<Venue> {
        self.venues.keys().copied().collect()
    }

    /// # Errors
    ///
    /// Returns an error if the venue is already registered, initializing the simulated exchange
    /// fails, or registering its execution client fails.
    pub fn add_venue(&mut self, config: SimulatedVenueConfig) -> anyhow::Result<()> {
        // `routing` and `frozen_account` flow to the exec client, so capture
        // them before the config is consumed by the exchange constructor.
        let venue = config.venue;
        if self.venues.contains_key(&venue) {
            anyhow::bail!("Venue {venue} is already registered");
        }

        let routing = Some(config.routing);
        let frozen_account = Some(config.frozen_account);
        let use_message_queue = config.use_message_queue;

        let exchange =
            SimulatedExchange::new(config, self.kernel.cache.clone(), self.kernel.clock.clone())?;
        let exchange = Rc::new(RefCell::new(exchange));

        let account_id = AccountId::from(format!("{venue}-001").as_str());

        let exec_client = BacktestExecutionClient::new(
            self.config.trader_id(),
            account_id,
            &exchange,
            self.kernel.cache.clone(),
            self.kernel.clock.clone(),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use a distinct Venue name for each registration (e.g. BINANCE vs BINANCE_FUTURES)
  2. Deduplicate venue configs before the registration loop
  3. Create a fresh BacktestEngine instead of reusing an already-configured one
  4. Guard registration with a contains/exists check on your own setup state

Example fix

// before
engine.add_venue(venue_binance.clone())?;
engine.add_venue(venue_binance.clone())?;  // duplicate
// after
if !registered.contains(&venue_binance.venue) {
    engine.add_venue(venue_binance.clone())?;
    registered.insert(venue_binance.venue);
}
Defensive patterns

Strategy: validation

Validate before calling

let mut seen = std::collections::HashSet::new();
for cfg in &venue_configs {
    if !seen.insert(cfg.venue) {
        return Err(format!("Duplicate venue in config: {}", cfg.venue));
    }
}

Try / catch

match std::panic::catch_unwind(|| engine.add_venue(cfg)) { ... } // prefer:
if let Err(e) = engine.add_venue(cfg) {
    if e.to_string().contains("already registered") {
        log::warn!("Venue {} registered twice; skipping", cfg.venue);
        return Ok(());
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Calling BacktestEngine::add_venue twice with SimulatedVenueConfig values whose `venue` fields are equal (e.g. two configs for BINARYBLOCK or "BINANCE"), or re-running setup code that registers venues without clearing the engine.

Common situations: Copy-pasted venue setup blocks; a loop over venue configs containing duplicates; re-invoking an add_venue setup function on an already-configured engine; accidentally using the same venue name for spot and futures.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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