nautechsystems/nautilus_trader · error

invalid price `{value}` at precision {precision}: {e}

Error message

invalid price `{value}` at precision {precision}: {e}

What it means

parse_optional_price converts a Lighter decimal price into a Nautilus Price at a fixed precision using Price::from_decimal_dp. Zero prices are treated as 'no price' and return Ok(None); any non-zero value that cannot be represented exactly at the given precision (excess decimal places, out-of-range magnitude) produces this error wrapping the underlying failure.

Source

Thrown at crates/adapters/lighter/src/websocket/parse.rs:1202

        return Ok(UnixNanos::default());
    }

    let millis = if timestamp <= UNIX_TIMESTAMP_SECONDS_MAX {
        timestamp * 1_000
    } else {
        timestamp
    };

    parse_millis_to_nanos(millis as u64)
}

fn parse_optional_price(value: Decimal, precision: u8) -> anyhow::Result<Option<Price>> {
    if value.is_zero() {
        return Ok(None);
    }
    Price::from_decimal_dp(value, precision)
        .map(Some)
        .map_err(|e| anyhow::anyhow!("invalid price `{value}` at precision {precision}: {e}"))
}

fn lighter_fee_to_commission(
    fee_ticks: Option<i32>,
    currency: Currency,
) -> Result<Money, LighterCommissionError> {
    let ticks = fee_ticks.unwrap_or(0);
    let amount = Decimal::new(i64::from(ticks), FEE_DECIMALS);
    Money::from_decimal(amount, currency).map_err(|e| LighterCommissionError::new(e.to_string()))
}

fn nautilus_order_side(side: LighterOrderSide) -> OrderSide {
    match side {
        LighterOrderSide::Buy => OrderSide::Buy,
        LighterOrderSide::Sell => OrderSide::Sell,
    }
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Round/quantize the Decimal to the target precision before calling: value.round_dp(precision), accepting the small quantization, or reject upstream if the difference is material.
  2. Verify the instrument's price precision used to compute the `precision` argument matches the exchange's current tick size; refresh the instrument if stale.
  3. Log the raw value and precision on failure to see whether the value or the precision is wrong.
  4. If Lighter genuinely emits finer prices after a market change, update the instrument definition in your catalog rather than hacking the parser.

Example fix

// before
Price::from_decimal_dp(value, precision)
    .map(Some)
    .map_err(|e| anyhow::anyhow!("invalid price `{value}` at precision {precision}: {e}"))

// after
let value = value.round_dp(u32::from(precision));
Price::from_decimal_dp(value, precision)
    .map(Some)
    .map_err(|e| anyhow::anyhow!("invalid price `{value}` at precision {precision}: {e}"))
Defensive patterns

Strategy: validation

Validate before calling

fn fits_precision(value: Decimal, precision: u8) -> bool {
    value.scale() <= u32::from(precision) && value.is_finite()
}

Try / catch

match parse_optional_price(raw, precision) {
    Ok(Some(p)) => p,
    Ok(None) => return Ok(None),
    Err(e) => { log::warn!("price {raw} unusable at precision {precision}: {e}"); return Ok(None); }
}

Prevention

When it happens

Trigger: parse_ws_order_status_report or lighter_order_shape receives a non-zero Lighter price whose decimal representation does not fit the instrument's price precision (e.g. 0.123456 with precision 2), or a value beyond Price's representable range.

Common situations: Exchange changes tick size and emits prices finer than the locally configured precision; instrument registered with wrong precision; a synthetic/derived price (e.g. stop trigger computed elsewhere) carries more dp than allowed; test fixtures using arbitrary precision decimals.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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