nautechsystems/nautilus_trader · error · anyhow::Error
Invalid `price_type`, was '{price_type}'
Error message
Invalid `price_type`, was '{price_type}' What it means
get_exchange_rate only supports Bid, Ask, and Mid price types for computing conversion rates. Any other PriceType value bails with this message. The check was relocated next to the quote-map validation so the identical-currency shortcut keeps its original precedence.
Source
Thrown at crates/common/src/xrate.rs:64
) -> 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 {
log::warn!("Skipping invalid pair string: {pair}");
continue;
}
if bid <= Decimal::ZERO || ask <= Decimal::ZERO {
// Both sides are required to build valid forward and reverse edges.View on GitHub (pinned to 18893faf8b)
Solutions
- Use PriceType::Mid (typical for conversion rates), Bid, or Ask.
- For last-trade-based rates, compute the rate from trade ticks instead of the xrate API.
- Validate the price_type in Python before calling py_get_exchange_rate.
Example fix
// before rate = py_get_exchange_rate(usd, eur, PriceType.LAST, bids, asks) // after rate = py_get_exchange_rate(usd, eur, PriceType.MID, bids, asks)
Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = {PriceType.BID, PriceType.ASK, PriceType.MID}
if price_type not in ALLOWED:
raise ValueError(f"price_type must be BID, ASK or MID, got {price_type}") Type guard
def is_fx_price_type(pt: PriceType) -> bool:
return pt in (PriceType.BID, PriceType.ASK, PriceType.MID) Try / catch
try:
rate = py_get_exchange_rate(base, quote, price_type, bids, asks)
except Exception as e:
if "Invalid `price_type`" in str(e):
rate = py_get_exchange_rate(base, quote, PriceType.MID, bids, asks) Prevention
- Default to PriceType.MID for currency conversion rates.
- Never pass trade-derived price types (e.g. LAST) to the xrate API.
- Validate enum config values against the supported set at startup.
When it happens
Trigger: Calling get_exchange_rate (or py_get_exchange_rate) with a PriceType outside Bid/Ask/Mid — e.g. Last or an out-of-enum value from Python bindings.
Common situations: Passing PriceType.LAST from Python strategy code that assumes all price types work for FX conversion; an enum value serialized from config that maps to an unsupported variant.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Not a subscription channel: {kind}
- PolymarketRtdsCryptoTwap metadata['window_seconds'] must be
- invalid `entry_order_type`, was {other}
- invalid `tp_order_type`, was {other}
- Quote maps must not be empty
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/849d884c96a44b01.
Report an issue: GitHub.