nautechsystems/nautilus_trader · error

{field} must be non-negative, was {value}

Error message

{field} must be non-negative, was {value}

What it means

Field sanity guard parse_millis_i64 used across Bybit WebSocket parsing: venue timestamps must be non-negative; a negative millis value indicates a malformed message and is rejected before conversion to UnixNanos.

Source

Thrown at crates/adapters/bybit/src/websocket/parse.rs:676

            delta,
            gamma,
            vega,
            theta,
            rho: 0.0, // Bybit doesn't provide rho
        },
        mark_iv: Some(mark_iv),
        bid_iv: Some(bid_iv),
        ask_iv: Some(ask_iv),
        underlying_price: Some(underlying_price),
        open_interest: Some(open_interest),
        ts_event,
        ts_init,
    })
}

pub(crate) fn parse_millis_i64(value: i64, field: &str) -> anyhow::Result<UnixNanos> {
    if value < 0 {
        Err(anyhow::anyhow!("{field} must be non-negative, was {value}"))
    } else {
        let nanos = (value as u64)
            .checked_mul(NANOSECONDS_IN_MILLISECOND)
            .ok_or_else(|| anyhow::anyhow!("millisecond timestamp overflowed"))?;
        Ok(UnixNanos::from(nanos))
    }
}

/// Parses a WebSocket kline payload into a [`Bar`].
///
/// # Errors
///
/// Returns an error if price or volume fields cannot be parsed or if the bar cannot be constructed.
pub fn parse_ws_kline_bar(
    kline: &BybitWsKline,
    instrument: &InstrumentAny,
    bar_type: BarType,
    timestamp_on_close: bool,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the raw WebSocket payload and the field named in the error for the negative value
  2. Treat -1/unknown sentinels as absent and skip or substitute a local receive timestamp
  3. Check for clock-skew arithmetic that subtracts a larger timestamp from a smaller one before calling the parser

Example fix

// before: passes sentinel straight through
let ts = parse_millis_i64(raw.ts, "ts")?;
// after: filter sentinels first
let ts = if raw.ts < 0 { recv_time_ns } else { parse_millis_i64(raw.ts, "ts")? };
Defensive patterns

Strategy: validation

Validate before calling

def valid_millis(v: int) -> bool:
    return v >= 0

Type guard

def is_non_negative(v) -> bool:
    return isinstance(v, int) and v >= 0

Try / catch

match parse_millis_i64(raw.ts, "ts") {
    Err(e) if e.to_string().contains("must be non-negative") => use_recv_time_fallback(),
    other => other?,
}

Prevention

When it happens

Trigger: Any WebSocket message (orderbook, trade tick, quote, orderbook deltas) carrying a negative millisecond timestamp field (e.g. `ts`, `transaction_time`) passed through parse_millis_i64.

Common situations: Clock skew producing negative derived deltas; upstream sending sentinel values like -1 for 'unknown'; corrupted or synthetic test payloads with negative times.

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