nautechsystems/nautilus_trader · error
Cannot add an `Instrument` object without first adding its a
Error message
Cannot add an `Instrument` object without first adding its associated venue {} What it means
The backtest engine requires the venue for an instrument to be registered before the instrument itself is added. add_instrument checks that the instrument's venue exists in the kernel; if not, it aborts so market data routing has an exchange to attach to. This preserves the invariant that every instrument belongs to a known simulated venue.
Source
Thrown at crates/backtest/src/engine.rs:377
exchange.borrow_mut().add_instrument(instrument.clone())?;
if let Some(expiration_ns) = instrument.expiration_ns() {
self.set_instrument_expiration_timer(exchange, instrument_id, expiration_ns)?;
}
if let Some(previous_expiration_ns) = previous_expiration_ns
&& instrument.expiration_ns() != Some(previous_expiration_ns)
&& !exchange
.borrow()
.has_unprocessed_instrument_expiration(previous_expiration_ns)
{
let timer_name = Self::instrument_expiration_timer_name(
instrument_id.venue,
previous_expiration_ns,
);
self.kernel.clock.borrow_mut().cancel_timer(&timer_name);
}
} else {
anyhow::bail!(
"Cannot add an `Instrument` object without first adding its associated venue {}",
instrument.id().venue
)
}
self.add_market_data_client_if_not_exists(instrument.id().venue);
self.kernel
.data_engine
.borrow_mut()
.process(instrument as &dyn Any);
log::info!(
"Added instrument {} to exchange {}",
instrument_id,
instrument_id.venue
);
Ok(())
}View on GitHub (pinned to 18893faf8b)
Solutions
- Call engine.add_venue(...) with a venue matching instrument.id().venue before add_instrument
- Verify instrument.id().venue string equals the venue passed to add_venue (case-sensitive exact match)
- Check that the instrument was built with the intended Venue, not a default or stale value
Example fix
// before engine.add_instrument(instrument)?; // after engine.add_venue(venue, execution_engine_config, risk_engine_config)?; engine.add_instrument(instrument)?;
Defensive patterns
Strategy: validation
Validate before calling
if !added_venues.contains(&instrument.id().venue) {
anyhow::bail!("venue {} not added; call add_venue first", instrument.id().venue);
}
engine.add_instrument(instrument)?; Try / catch
match engine.add_instrument(instrument) {
Ok(()) => {},
Err(e) if e.to_string().contains("without first adding its associated venue") => {
eprintln!("add the venue before the instrument: {e}");
}
Err(e) => return Err(e),
} Prevention
- Always call add_venue before any add_instrument call in setup code
- Assert venue registration in a setup helper that adds both together
- Compare venue strings exactly (case and suffix) between venue config and instrument
When it happens
Trigger: Calling engine.add_instrument(instrument) without a prior add_venue(...) for instrument.id().venue, or adding an instrument whose venue string does not exactly match the configured venue name.
Common situations: Copying an instrument from a live config whose venue name differs (e.g. 'BINANCE' vs 'BINANCE_FUTURES'); forgetting add_venue in a new script; instrument built for a venue that was never configured.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Instrument {first_instrument_id} for the given data not foun
- Data has been added but not sorted, call `engine.sort_data()
- Cannot handle command: {command:?}
- Latency model should be initialized
- Execution client should be initialized
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/10daeea416c18b9e.
Report an issue: GitHub.