nautechsystems/nautilus_trader · error
instrument metadata ID {} does not match {instrument_id}
Error message
instrument metadata ID {} does not match {instrument_id} What it means
`resolve_precision` looks up cached instrument metadata for an order symbol to recover price/size precision during recovery reconciliation. Beyond a missing entry, it enforces an internal invariant with `anyhow::ensure!`: the cached instrument's ID must equal the instrument ID constructed from the symbol and product type (`format_instrument_id`). A mismatch means the cache is keyed by symbol but holds an instrument belonging to a different market/product, so precisions cannot be trusted and the open-order report fails (triggering another recovery attempt).
Source
Thrown at crates/adapters/binance/src/futures/websocket/streams/recovery.rs:444
if !open_ok && !algo_ok {
anyhow::bail!("recovery reconcile failed: both REST queries returned errors");
}
log::info!("Recovery reconcile emitted {emitted} OrderStatusReport(s)");
Ok(())
}
fn resolve_precision(
instruments: &DashMap<ustr::Ustr, crate::futures::http::client::BinanceFuturesInstrument>,
symbol_ustr: &ustr::Ustr,
product_type: BinanceProductType,
) -> anyhow::Result<(InstrumentId, u8, u8)> {
let instrument_id = format_instrument_id(symbol_ustr, product_type);
let instrument = instruments
.get(symbol_ustr)
.map(|instrument| instrument.value().clone())
.with_context(|| format!("missing instrument metadata for {instrument_id}"))?;
anyhow::ensure!(
instrument.id() == instrument_id,
"instrument metadata ID {} does not match {instrument_id}",
instrument.id()
);
let (price_precision, size_precision) = instrument.precisions()?;
Ok((instrument_id, price_precision, size_precision))
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use axum::{
Json, Router,
extract::State,
http::{StatusCode, Uri},
response::{IntoResponse, Response},View on GitHub (pinned to 18893faf8b)
Solutions
- Check the log: compare the cached instrument ID (first `{}`) against the expected ID; identify which product type's cache was loaded.
- Rebuild the instruments cache from the correct product type's exchangeInfo before recovery.
- Ensure the cache key includes product type (or use separate maps for USDM/COINM) so symbols cannot collide.
- Verify `format_instrument_id` and the cache population path use identical symbol normalization (case, suffixes).
Example fix
// before
anyhow::ensure!(
instrument.id() == instrument_id,
"instrument metadata ID {} does not match {instrument_id}",
instrument.id()
);
// after (fix the cache keying so the invariant holds)
let key = (symbol_ustr, product_type); // key instruments by symbol AND product type
let instrument = instruments.get(&key)...; Defensive patterns
Strategy: validation
Validate before calling
// Rust: verify cache consistency before recovery
fn cache_consistent(instruments: &DashMap<Ustr, AnyInstrument>, product: ProductType) -> bool {
instruments.iter().all(|e| {
let expected = format_instrument_id(e.key(), product);
e.value().id() == expected
})
} Prevention
- Key the instruments cache by (symbol, product_type) or keep separate maps per market
- Rebuild the cache from the correct exchangeInfo when switching product type
- Use identical symbol normalization in cache population and format_instrument_id
- Test mixed USDT-M/COIN-M recovery paths to catch symbol collisions
When it happens
Trigger: `emit_open_order_reports` resolves precision for a returned open order; the instruments cache contains an entry for the symbol whose `instrument.id()` differs from `format_instrument_id(symbol, product_type)` — e.g. a USDT-M instrument cached under a symbol also used by COIN-M, or a stale cache built with a different product type.
Common situations: Mixed USDT-M/COIN-M setups where the instruments map was populated from the wrong product's exchange info; symbol reuse across markets (e.g. same base asset on both); cache not invalidated after switching product type.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- incompatible cached {position_side:?} position IDs for {inst
- actual order ID mismatch: expected {expected_actual_order_id
- actual order symbol mismatch: expected {}, was {}
- actual order side mismatch: expected {:?}, was {:?}
- position_side LONG conflicts with negative position_amt
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7f68d4351678936c.
Report an issue: GitHub.