nautechsystems/nautilus_trader · error

Scaled result exceeds 256-bit range

Error message

Scaled result exceeds 256-bit range

What it means

`mul_div_scaled` computes a high-precision a*b/denominator with extra decimal-scale correction steps. During each scale step the intermediate `quotient * scale` must still fit in U256; the library throws this error when that checked multiplication overflows the 256-bit range.

Source

Thrown at crates/model/src/defi/tick_map/full_math.rs:130

        // is no longer required.
        let result = prod_0 * inv;

        Ok(result)
    }

    pub(crate) fn mul_div_scaled(
        a: U256,
        b: U256,
        denominator: U256,
        scales: &[U256],
    ) -> anyhow::Result<U256> {
        let mut quotient = Self::mul_div(a, b, denominator)?;
        let mut remainder = a.mul_mod(b, denominator);

        for &scale in scales {
            let scaled_quotient = quotient
                .checked_mul(scale)
                .ok_or_else(|| anyhow::anyhow!("Scaled result exceeds 256-bit range"))?;
            let scaled_remainder = Self::mul_div(remainder, scale, denominator)?;
            quotient = scaled_quotient
                .checked_add(scaled_remainder)
                .ok_or_else(|| anyhow::anyhow!("Scaled result exceeds 256-bit range"))?;
            remainder = remainder.mul_mod(scale, denominator);
        }

        Ok(quotient)
    }

    pub(crate) fn check_decimal_exponent(exponent: u8) -> anyhow::Result<()> {
        anyhow::ensure!(
            exponent <= DECIMAL_EXPONENT_MAX,
            "Decimal exponent {exponent} exceeds supported maximum {DECIMAL_EXPONENT_MAX}"
        );
        Ok(())
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Normalize/scale inputs before calling (use smaller intermediate values or divide early).
  2. Pre-check that quotient <= U256::MAX / scale before invoking.
  3. Reduce the decimal exponent used for scaling if the math allows (recompute the correction differently).
  4. Use U512 internally for the scaled step and convert back only if it fits.

Example fix

// before
let q = mul_div_scaled(a, b, denominator, &[scale])?;
// after
let prelim = FullMath::mul_div(a, b, denominator)?;
ensure!(prelim <= U256::MAX / scale, "inputs too large for scaled mul_div");
let q = mul_div_scaled(a, b, denominator, &[scale])?;
Defensive patterns

Strategy: validation

Validate before calling

fn can_scale(quotient: U256, scale: U256) -> bool {
    quotient.checked_mul(scale).is_some()
}

Try / catch

match mul_div_scaled(&a, &b, &denominator, scales) {
    Ok(q) => q,
    Err(e) if e.to_string().contains("exceeds 256-bit") => normalize_inputs_and_retry(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Inputs where the preliminary quotient from `mul_div` is so large that multiplying by the decimal scale factor (10^k) exceeds 2^256 — extremely large `a` or `b` combined with a small denominator and a large scale.

Common situations: Feeding raw on-chain uint256 amounts near the U256 maximum into price/liquidity math with high decimal-exponent scales (e.g. 18-decimal tokens) rather than normalized values.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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