nautechsystems/nautilus_trader · warning · anyhow::Error
Missing exchange in record: {e}
Error message
Missing exchange in record: {e} What it means
When `use_exchange_as_venue` is enabled and the instrument's venue is GLBX, the adapter replaces the venue with the record's specific exchange code. If the raw DBN InstrumentDefMsg has no exchange field populated, `msg.exchange()` fails and this error wraps it. It indicates malformed/absent data in the record rather than an API call failure.
Source
Thrown at crates/adapters/databento/src/historical.rs:320
let metadata = decoder.metadata().clone();
let mut metadata_cache = MetadataCache::new(metadata);
let mut instruments = Vec::new();
while let Some(msg) = decoder.decode_record::<dbn::InstrumentDefMsg>().await? {
let record = dbn::RecordRef::from(msg);
let sym_map = self.symbol_venue_map.load();
let mut instrument_id = decode_nautilus_instrument_id(
&record,
&mut metadata_cache,
&self.publisher_venue_map,
&sym_map,
)?;
if self.use_exchange_as_venue && instrument_id.venue == Venue::GLBX() {
let exchange = msg
.exchange()
.map_err(|e| anyhow::anyhow!("Missing exchange in record: {e}"))?;
let venue = Venue::from_code(exchange)
.map_err(|e| anyhow::anyhow!("Venue not found for exchange {exchange}: {e}"))?;
instrument_id.venue = venue;
}
match decode_instrument_def_msg(msg, instrument_id, None, None) {
Ok(Some(instrument)) => instruments.push(instrument),
Ok(None) => {} // Decoder logged a warning for the unsupported class
Err(e) => anyhow::bail!("Failed to decode instrument {instrument_id}: {e}"),
}
}
for instrument in &instruments {
self.price_precisions
.insert(instrument.id().symbol, instrument.price_precision());
}
Ok(instruments)View on GitHub (pinned to 18893faf8b)
Solutions
- Disable `use_exchange_as_venue` (pass false) so the default GLBX venue is kept
- Check the dataset/record actually corresponds to CME Globex data with exchange codes
- Update the databento/dbn crates to the latest version and refetch
- Inspect the raw record to confirm the exchange field is genuinely empty
Example fix
// before let client = DatabentoHistoricalClient::new(cred, path, clock, true)?; // use_exchange_as_venue = true // after let client = DatabentoHistoricalClient::new(cred, path, clock, false)?;
Defensive patterns
Strategy: fallback
Validate before calling
// if records may lack exchange codes, do not request per-exchange venues let use_exchange_as_venue = false;
Type guard
fn msg_has_exchange(msg: &dbn::InstrumentDefMsg) -> bool { msg.exchange().is_ok() } Try / catch
let venue = match msg.exchange() {
Ok(code) => Venue::from_code(code).unwrap_or(Venue::GLBX()),
Err(_) => Venue::GLBX(),
}; Prevention
- Enable use_exchange_as_venue only for datasets known to populate exchange fields
- Inspect raw DBN records when migrating datasets or crate versions
- Default to GLBX venue rather than hard-requiring the exchange field
- Pin dbn crate versions and review changelogs for field changes
When it happens
Trigger: Calling `get_range_instruments` with `use_exchange_as_venue = true` on a GLBX instrument definition record whose `exchange` field is empty/missing.
Common situations: Datasets or record versions where the exchange field is not populated; decoding non-Globex data that was tagged GLBX; changes in DBN schema between crate versions.
Related errors
- Failed to get range: {e}
- Venue not found for exchange {exchange}: {e}
- Invalid `{SCHEMA_PARAM}` '{schema}'. Must be one of: {allowe
- Invalid negative `contract_multiplier`: {value}
- `rtype` is not a supported bar aggregation, was {msg.hd.rtyp
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/264b27eb40642281.
Report an issue: GitHub.