nautechsystems/nautilus_trader · error · anyhow::Error
Quote maps must have equal lengths
Error message
Quote maps must have equal lengths
What it means
get_exchange_rate requires the bid and ask quote maps to be parallel: the same currency-pair keys in the same quantities. Mismatched lengths mean the two price series cannot be zipped into consistent graph edges, so the function bails. This preserves the invariant that each pair has both a bid and an ask quote.
Source
Thrown at crates/common/src/xrate.rs:58
pub fn get_exchange_rate(
from_currency: Ustr,
to_currency: Ustr,
price_type: PriceType,
quotes_bid: AHashMap<Ustr, Decimal>,
mut quotes_ask: AHashMap<Ustr, Decimal>,
) -> anyhow::Result<Option<Decimal>> {
if from_currency == to_currency {
// When the source and target currencies are identical,
// no conversion is needed; return an exchange rate of one.
return Ok(Some(Decimal::ONE));
}
if quotes_bid.is_empty() || quotes_ask.is_empty() {
anyhow::bail!("Quote maps must not be empty");
}
if quotes_bid.len() != quotes_ask.len() {
anyhow::bail!("Quote maps must have equal lengths");
}
// Validated here, in the same position as the price-type match this replaced, so the
// identical-currency shortcut and the quote-map errors keep their original precedence.
if !matches!(price_type, PriceType::Bid | PriceType::Ask | PriceType::Mid) {
anyhow::bail!("Invalid `price_type`, was '{price_type}'");
}
// Construct a graph: each currency maps to its neighbors and corresponding conversion rate
let mut graph: AHashMap<Ustr, Vec<(Ustr, Decimal)>> = AHashMap::new();
for (pair, bid) in quotes_bid {
let ask = quotes_ask
.remove(&pair)
.ok_or_else(|| anyhow::anyhow!("Missing ask quote for pair {pair}"))?;
let parts: Vec<&str> = pair.split('/').collect();
if parts.len() != 2 {View on GitHub (pinned to 18893faf8b)
Solutions
- Build both maps from the same pair set — filter bid and ask maps with identical criteria.
- Log/inspect which pairs exist in only one map and repair the data source for the missing side.
- Defensively intersect the key sets before calling, ensuring lengths match.
Example fix
// before
let rate = get_exchange_rate(usd, eur, pt, &all_bids, &all_asks)?; // lengths differ
// after
let pairs: HashSet<_> = all_bids.keys().collect::<HashSet<_>>()
.intersection(&all_asks.keys().collect()).cloned().collect();
let bids: AHashMap<_,_> = pairs.iter().map(|k| (*k, all_bids[k])).collect();
let asks: AHashMap<_,_> = pairs.iter().map(|k| (*k, all_asks[k])).collect();
let rate = get_exchange_rate(usd, eur, pt, &bids, &asks)?; Defensive patterns
Strategy: validation
Validate before calling
if set(bid_quotes) != set(ask_quotes):
raise ValueError("bid and ask quote maps must cover identical currency pairs")
if len(bid_quotes) != len(ask_quotes):
raise ValueError("bid and ask quote maps must have equal lengths") Try / catch
match get_exchange_rate(base, quote, pt, &bids, &asks) {
Err(e) if e.to_string().contains("equal lengths") => {
log::warn!("bid/ask quote maps out of sync: {e}");
None
}
other => other.ok().flatten(),
} Prevention
- Build both maps from one snapshot pass so they always share the same key set.
- Filter bid and ask maps with identical staleness/price criteria.
- Assert map key equality in tests for any code that prepares quote maps.
When it happens
Trigger: Calling get_exchange_rate with quotes_bid.len() != quotes_ask.len() — e.g. bid quotes cached for pairs A/B, C/D but ask quotes only for A/B; building the maps from different time windows or instruments.
Common situations: Partial feed failures where one side of quotes is missing; filtering one map by price type or staleness without filtering the other; mixing snapshot times so one map has extra pairs.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Quote maps must not be empty
- Invalid `price_type`, was '{price_type}'
- Invalid quote rate for pair {pair}, was {value}
- Unsupported `OrderSide` for Binance: {value:?}
- invalid OrderSide: must be Buy or Sell, was {side}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/891db56a023a40bb.
Report an issue: GitHub.