nautechsystems/nautilus_trader · error

invalid candle open: {e}

Error message

invalid candle open: {e}

What it means

parse_ws_bar converts the candle's open decimal into a Price quantized to the instrument's price_precision via Price::from_decimal_dp. If the decimal is invalid for that precision (e.g. more fractional digits than precision allows, or conversion failure), the error is wrapped as 'invalid candle open'.

Source

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

}

/// Parses a Lighter WebSocket candle into a Nautilus [`Bar`] with `ts_event` set to the bar open.
///
/// # Errors
///
/// Returns an error if the OHLCV decimals overflow the instrument's precision, or if the
/// timestamp cannot be converted.
pub fn parse_ws_bar(
    instrument: &InstrumentAny,
    candle: &LighterWsCandle,
    resolution: LighterCandleResolution,
    ts_init: UnixNanos,
) -> anyhow::Result<Bar> {
    let price_precision = instrument.price_precision();
    let size_precision = instrument.size_precision();

    let open = Price::from_decimal_dp(candle.o, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid candle open: {e}"))?;
    let high = Price::from_decimal_dp(candle.h, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid candle high: {e}"))?;
    let low = Price::from_decimal_dp(candle.l, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid candle low: {e}"))?;
    let close = Price::from_decimal_dp(candle.c, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid candle close: {e}"))?;
    let volume = Quantity::from_decimal_dp(candle.v, size_precision)
        .map_err(|e| anyhow::anyhow!("invalid candle volume: {e}"))?;

    let t_ms = u64::try_from(candle.t)
        .map_err(|_| anyhow::anyhow!("negative candle timestamp: {}", candle.t))?;
    let ts_event = parse_millis_to_nanos(t_ms)?;

    let bar_type = BarType::new(
        instrument.id(),
        resolution.to_bar_spec(),
        AggregationSource::External,
    );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set the instrument's price_precision to match the venue's actual price precision for the market
  2. Round/quantize the candle open to price_precision before constructing the Bar
  3. Log candle.o and the underlying conversion error to identify the offending value
  4. Confirm the correct instrument/subscription mapping

Example fix

// before
let open = Price::from_decimal_dp(candle.o, price_precision)?;
// after
let open = Price::from_decimal_dp(candle.o.round_dp(price_precision as u32), price_precision)?;
Defensive patterns

Strategy: validation

Validate before calling

let open = candle.o.round_dp(instrument.price_precision() as u32);

Try / catch

match parse_ws_bar(candle, instrument, ...) {
    Err(e) if e.to_string().contains("invalid candle open") => {
        log::warn!("bad candle open {}, skipping bar: {e}", candle.o);
    }
    other => other?,
}

Prevention

When it happens

Trigger: A candle open value from the WebSocket that cannot be represented at the instrument's price_precision — extra decimal digits beyond precision, or a malformed/negative value.

Common situations: Instrument precision misconfigured lower than venue's actual precision; venue formatting change sending more decimals; wrong instrument registered for the candle subscription.

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


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