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
- Use a distinct Venue name for each registration (e.g. BINANCE vs BINANCE_FUTURES)
- Deduplicate venue configs before the registration loop
- Create a fresh BacktestEngine instead of reusing an already-configured one
- 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
- Deduplicate venue configs before setup
- Never re-run venue registration on an already-configured engine
- Use distinct venue names for spot vs futures
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
- Cannot handle command: {command:?}
- Latency model should be initialized
- Execution client should be initialized
- Matching engine not found for instrument {order_instrument_i
- Matching engine not found for instrument {instrument_id}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4d0186593e1b2654.
Report an issue: GitHub.