{"record":{"id":"3f4a3f82c4fbcc86","repo":"nautechsystems/nautilus_trader","slug":"invalid-candle-bar-e","errorCode":null,"errorMessage":"invalid candle bar: {e}","messagePattern":"invalid candle bar: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/websocket/parse.rs","lineNumber":421,"sourceCode":"    let low = Price::from_decimal_dp(candle.l, price_precision)\n        .map_err(|e| anyhow::anyhow!(\"invalid candle low: {e}\"))?;\n    let close = Price::from_decimal_dp(candle.c, price_precision)\n        .map_err(|e| anyhow::anyhow!(\"invalid candle close: {e}\"))?;\n    let volume = Quantity::from_decimal_dp(candle.v, size_precision)\n        .map_err(|e| anyhow::anyhow!(\"invalid candle volume: {e}\"))?;\n\n    let t_ms = u64::try_from(candle.t)\n        .map_err(|_| anyhow::anyhow!(\"negative candle timestamp: {}\", candle.t))?;\n    let ts_event = parse_millis_to_nanos(t_ms)?;\n\n    let bar_type = BarType::new(\n        instrument.id(),\n        resolution.to_bar_spec(),\n        AggregationSource::External,\n    );\n\n    Bar::new_checked(bar_type, open, high, low, close, volume, ts_event, ts_init)\n        .map_err(|e| anyhow::anyhow!(\"invalid candle bar: {e}\"))\n}\n\nfn parse_book_level_delta(\n    level: &LighterPriceLevel,\n    instrument: &InstrumentAny,\n    side: OrderSide,\n    sequence: u64,\n    ts_event: UnixNanos,\n    ts_init: UnixNanos,\n    flags: u8,\n) -> anyhow::Result<OrderBookDelta> {\n    let price = price_from_decimal(level.price, instrument.price_precision())?;\n    let size = quantity_from_decimal(level.size, instrument.size_precision())?;\n    let action = if flags & RecordFlag::F_SNAPSHOT as u8 != 0 {\n        BookAction::Add\n    } else if size.is_zero() {\n        BookAction::Delete\n    } else {","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/websocket/parse.rs#L403-L439","documentation":"parse_ws_bar builds a NautilusTrader Bar from a Lighter candlestick websocket message and validates the OHLCV combination via Bar::new_checked. When any component (open/high/low/close price, volume, or timestamps) violates Bar invariants — e.g. a price with excess precision for the instrument, a negative/NaN value, or zero-precision mismatch — the check fails and the parse is aborted with this anyhow error so the bad candle is not emitted downstream.","triggerScenarios":"A candle update arrives over the Lighter websocket whose price or volume fields, after parsing into Price/Quantity at the instrument's precision, do not satisfy Bar::new_checked invariants (e.g. value rounds out of range at instrument precision, negative volume, or inconsistent price precision).","commonSituations":"Exchange sends a candle with more decimal places than the instrument's configured price precision; instrument was registered with wrong precision/size in the cache; a degenerate or malformed candle (empty or zeroed fields) arrives during reconnect/snapshot transitions; instrument precision changed on the exchange but the local definition is stale.","solutions":["Check the instrument definition in the cache matches the exchange's current price/increment precision for that market (instrument_id must come from the same catalog used at subscribe time).","Log the raw candle payload (open/high/low/close/volume/ts) before Bar::new_checked to identify which field violates the invariants.","If the exchange changed tick size, refresh the instrument (re-request instrument definitions) and rebuild any catalog entry with stale precision.","If the bad candle is a transient exchange artifact (e.g. a partial snapshot candle), treat it as a skipped update and continue consuming subsequent bars instead of failing the stream."],"exampleFix":"// before\nBar::new_checked(bar_type, open, high, low, close, volume, ts_event, ts_init)\n    .map_err(|e| anyhow::anyhow!(\"invalid candle bar: {e}\"))\n\n// after\nlet open = Price::new(open_raw.round_dp(instrument.price_increment_precision).as_f64(), instrument.price_precision)?;\n// ensure each price/quantity is built at the instrument's exact precision before construction\nBar::new_checked(bar_type, open, high, low, close, volume, ts_event, ts_init)\n    .map_err(|e| anyhow::anyhow!(\"invalid candle bar: {e}\"))","handlingStrategy":"validation","validationCode":"fn is_valid_candle(open: f64, high: f64, low: f64, close: f64, volume: f64) -> bool {\n    [open, high, low, close, volume].iter().all(|v| v.is_finite() && *v >= 0.0)\n        && high >= low\n        && high >= open && high >= close\n        && low <= open && low <= close\n}","typeGuard":null,"tryCatchPattern":"match parse_ws_bar(...) {\n    Ok(bar) => process(bar),\n    Err(e) => { log::warn!(\"skipping malformed candle: {e}\"); continue; }\n}","preventionTips":["Keep instrument definitions (price precision, size increment) in sync with the exchange's current tick rules.","Validate OHLC relationships (high >= max(open,close), low <= min(open,close)) on raw payloads before parsing.","Log the raw candle message before conversion so failures are diagnosable."],"tags":["websocket","parsing","candlestick","rust","data-integrity"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}