nautechsystems/nautilus_trader · error
Underflow occurred when subtracting `Price`
Error message
Underflow occurred when subtracting `Price`
What it means
Price implements Sub by checked_sub on raw fixed-point integers; if the result is below the representable range (most commonly a negative result, since Price is unsigned in normal builds), checked_sub returns None and this expect panics. Subtracting a larger Price from a smaller one is the typical cause.
Source
Thrown at crates/model/src/types/price.rs:700
.checked_add(rhs.raw)
.expect("Overflow occurred when adding `Price`"),
precision: self.precision.max(rhs.precision),
}
}
}
impl Sub for Price {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
assert!(
raw_scales_match(self.precision, rhs.precision),
"Cannot subtract `Price` values with mismatched decimal scales"
);
Self {
raw: self
.raw
.checked_sub(rhs.raw)
.expect("Underflow occurred when subtracting `Price`"),
precision: self.precision.max(rhs.precision),
}
}
}
impl Add<Decimal> for Price {
type Output = Decimal;
fn add(self, rhs: Decimal) -> Self::Output {
self.as_decimal() + rhs
}
}
impl Sub<Decimal> for Price {
type Output = Decimal;
fn sub(self, rhs: Decimal) -> Self::Output {
self.as_decimal() - rhs
}
}View on GitHub (pinned to 18893faf8b)
Solutions
- Order operands: compute (max - min) or take .abs() semantics explicitly via Decimal before constructing a Price.
- Compute differences in Decimal (price.as_decimal()) which supports negatives, and only build a Price from non-negative results.
- Guard with a comparison (if a >= b { a - b } else { ... }) before using the - operator.
- For signed deltas, use a dedicated signed type rather than Price.
Example fix
// before
let spread = ask - bid; // panics when bid > ask (negative spread)
// after
let spread = if ask >= bid { ask - bid } else { (bid - ask).neg_price() /* or track sign separately */ }; Defensive patterns
Strategy: validation
Validate before calling
// Rust
fn checked_price_sub(a: Price, b: Price) -> Option<Price> {
(a.precision == b.precision && a.raw >= b.raw).then(|| Price { raw: a.raw - b.raw, precision: a.precision })
} Try / catch
// Compare before subtracting:
let spread = if ask >= bid { ask - bid } else { Decimal::ZERO }; // or track sign via Decimal Prevention
- Never subtract Prices without knowing which is larger; Price cannot represent negative values.
- Use Decimal (as_decimal) for signed deltas and spreads.
- Sort operands (max/min) when computing magnitude of differences.
- Add assertions/tests on spread logic where bid > ask can occur with crossed books.
When it happens
Trigger: Using the - operator (impl Sub for Price) where lhs.raw < rhs.raw (negative difference) or the difference falls below PRICE_RAW_MIN, with matching precision required beforehand.
Common situations: Computing price deltas without knowing which side is larger; spread calculations where the spread can legitimately be negative; sorting/branching bugs that swap operands.
Related errors
- Underflow occurred when subtracting `Quantity`
- Liquidity subtraction underflow: x={current}, y={y}, delta={
- precision exceeded i8 range
- raw wei value exceeds 128-bit range
- effective raw scale should fit in PriceRaw
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4f4369470bb658c7.
Report an issue: GitHub.