nautechsystems/nautilus_trader · error
negative candle timestamp: {}
Error message
negative candle timestamp: {} What it means
parse_ws_bar casts the candle's timestamp (milliseconds, as i64) to u64 before converting to nanoseconds. A negative timestamp cannot be cast losslessly, so the adapter raises 'negative candle timestamp' rather than producing a corrupted event time.
Source
Thrown at crates/adapters/lighter/src/websocket/parse.rs:411
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,
);
Bar::new_checked(bar_type, open, high, low, close, volume, ts_event, ts_init)
.map_err(|e| anyhow::anyhow!("invalid candle bar: {e}"))
}
fn parse_book_level_delta(
level: &LighterPriceLevel,
instrument: &InstrumentAny,
side: OrderSide,
sequence: u64,
ts_event: UnixNanos,View on GitHub (pinned to 18893faf8b)
Solutions
- Log the raw candle payload and check the venue schema for the timestamp field
- Reject or skip messages with negative timestamps upstream before parsing
- Update the parser if the venue changed the timestamp format/epoch
- Validate deserialization maps the correct JSON field to candle.t
Example fix
// before
let t_ms = u64::try_from(candle.t)?;
// after
if candle.t < 0 { log::warn!("skipping candle with negative timestamp {}", candle.t); return Ok(None); }
let t_ms = u64::try_from(candle.t)?; Defensive patterns
Strategy: validation
Validate before calling
if candle.t < 0 {
log::warn!("negative candle timestamp {}", candle.t);
return; // skip bad candle
} Type guard
fn has_valid_timestamp(candle: &WsCandle) -> bool { candle.t >= 0 } Try / catch
match parse_ws_bar(candle, instrument, ...) {
Err(e) if e.to_string().contains("negative candle timestamp") => {
log::warn!("skipping malformed candle: {e}");
}
other => other?,
} Prevention
- Validate timestamps on ingest before parsing
- Watch for venue schema changes to the timestamp field
- Skip-and-log malformed candles instead of failing the stream
When it happens
Trigger: A candle message with candle.t < 0 — malformed venue payload, integer underflow upstream, or a misparsed field causing the signed timestamp to go negative.
Common situations: Venue protocol/schema change; clock or epoch issues producing negative times; deserialization bug mapping the wrong JSON field to t.
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
- {field} must be non-negative, was {value}
- millisecond timestamp overflowed
- Missing server timestamp (us_out) in response
- invalid candle open: {e}
- invalid candle high: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/0abb659b623c4a86.
Report an issue: GitHub.