nautechsystems/nautilus_trader · error

Failed to decode instrument {instrument_id}: {e}

Error message

Failed to decode instrument {instrument_id}: {e}

What it means

`get_range_instruments` (used by `seed_price_precision_if_needed`) fetches instrument definitions from Databento and decodes each into a Nautilus `InstrumentDef`. When `decode_instrument_def_msg` fails for a specific instrument, the error is wrapped with the instrument_id and propagated, aborting the whole definitions request rather than skipping the instrument.

Source

Thrown at crates/adapters/databento/src/historical.rs:329

                &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)
    }

    /// Fetches quote ticks for the given parameters.
    ///
    /// # Errors
    ///
    /// Returns an error if the API request or data processing fails.
    pub async fn get_range_quotes(
        &self,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the wrapped inner error and instrument_id to identify the failing record; request that definition alone to inspect it.
  2. Remove or pre-validate the offending symbol from the request.
  3. If the instrument class is unsupported, ensure the decoder is expected to skip (Ok(None)) — check which check returned Err instead.
  4. Wrap the seeding call so a failing instrument can be skipped instead of failing the whole data request.

Example fix

// before
match decode_instrument_def_msg(msg, instrument_id, None, None)? { ... }
// after
match decode_instrument_def_msg(msg, instrument_id, None, None) {
    Ok(Some(i)) => instruments.push(i),
    Ok(None) => {}
    Err(e) => { log::warn!("skipping {instrument_id}: {e}"); continue; }
}
Defensive patterns

Strategy: try-catch

Try / catch

match get_range_instruments(...).await {
    Ok(defs) => defs,
    Err(e) => {
        // e contains "Failed to decode instrument <id>: <cause>"
        let id = extract_instrument_id(&e.to_string());
        log::warn!("skipping undecodable instrument {id}: {e}");
        Vec::new()
    }
}

Prevention

When it happens

Trigger: Calling a historical data range method that needs price precision seeding, where one of the returned definition records fails to decode (e.g. undefined required price/timestamp/multiplier, or unsupported instrument class returning Err).

Common situations: Requesting data for a symbol batch where a single broken/unusual definition poisons the entire call; asset classes whose definition fields don't match the decoder's expectations; Databento schema changes.

Understand the failure class

Related errors


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