nautechsystems/nautilus_trader · error

non-positive candle open `{}`

Error message

non-positive candle open `{}`

What it means

parse_candle_bar validates that a Lighter candle's open price is strictly positive before building a Bar. Lighter data must have OHLC values > 0 (Price cannot be zero or negative); a non-positive open means corrupt, placeholder, or malformed API data. The error includes the offending value.

Source

Thrown at crates/adapters/lighter/src/http/parse.rs:187

        trade_id,
        ts_event,
        ts_init,
    )
    .context("failed to construct TradeTick from Lighter trade")
}

/// Parses a Lighter candle into a Nautilus [`Bar`].
///
/// # Errors
///
/// Returns an error if any price, volume, or timestamp field cannot be converted.
pub fn parse_candle_bar(
    candle: &LighterCandle,
    bar_type: BarType,
    instrument: &InstrumentAny,
    ts_init: UnixNanos,
) -> anyhow::Result<Bar> {
    anyhow::ensure!(
        candle.open > Decimal::ZERO,
        "non-positive candle open `{}`",
        candle.open
    );
    anyhow::ensure!(
        candle.high > Decimal::ZERO,
        "non-positive candle high `{}`",
        candle.high
    );
    anyhow::ensure!(
        candle.low > Decimal::ZERO,
        "non-positive candle low `{}`",
        candle.low
    );
    anyhow::ensure!(
        candle.close > Decimal::ZERO,
        "non-positive candle close `{}`",
        candle.close

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Skip or filter candles with open <= 0 before calling parse_candle_bar/parse_bars
  2. Check the upstream Lighter API response for corrupt or placeholder candle records
  3. Confirm the instrument/market is active and has traded (delisted or never-traded markets may yield zeros)
  4. Validate fixture/test data contains positive OHLC values

Example fix

// before
let bars: Vec<Bar> = candles.iter().map(|c| parse_candle_bar(c, bar_type, &inst, ts_init)).collect::<Result<_>>()?;
// after
let bars: Vec<Bar> = candles.iter().filter(|c| c.open > Decimal::ZERO).map(|c| parse_candle_bar(c, bar_type, &inst, ts_init)).collect::<Result<_>>()?;
Defensive patterns

Strategy: validation

Validate before calling

if candle.open <= Decimal::ZERO {
    return Err(anyhow::anyhow!("skip candle: non-positive open {}", candle.open));
}

Type guard

fn has_positive_ohlc(c: &LighterCandle) -> bool {
    c.open > Decimal::ZERO && c.high > Decimal::ZERO && c.low > Decimal::ZERO && c.close > Decimal::ZERO
}

Try / catch

let bar = match parse_candle_bar(&candle, bar_type, instrument, ts_init) {
    Ok(b) => b,
    Err(e) if e.to_string().contains("non-positive candle open") => continue,
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: parse_candle_bar receiving a LighterCandle whose open field is <= 0 (zero or negative Decimal), typically via parse_bars processing a /candles HTTP response.

Common situations: Exchange API returning zeroed-out candles for illiquid/new markets or delisted pairs; fixtures or mocks with placeholder zeros; upstream data glitches during outages.

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/b6f09bfff1d8b986. Report an issue: GitHub.