nautechsystems/nautilus_trader · error · anyhow::Error

invalid negative Binance {field} timestamp: {value}

Error message

invalid negative Binance {field} timestamp: {value}

What it means

Binance timestamps are converted to UnixNanos via checked constructors; a negative i64 cannot be represented as pre-epoch nanos in this domain type and is rejected. Binance commonly uses -1 as a 'not available' sentinel, which some fields legitimately carry and this strict parse refuses.

Source

Thrown at crates/adapters/binance/src/common/parse.rs:90

const CONTRACT_TYPE_CURRENT_QUARTER: &str = "CURRENT_QUARTER";
const CONTRACT_TYPE_NEXT_QUARTER: &str = "NEXT_QUARTER";

pub(crate) fn parse_millis(value: i64, field: &str) -> anyhow::Result<UnixNanos> {
    parse_timestamp(value, UnixNanos::from_millis_checked(value), field)
}

pub(crate) fn parse_micros(value: i64, field: &str) -> anyhow::Result<UnixNanos> {
    parse_timestamp(value, UnixNanos::from_micros_checked(value), field)
}

fn parse_timestamp(
    value: i64,
    timestamp: Option<UnixNanos>,
    field: &str,
) -> anyhow::Result<UnixNanos> {
    timestamp.ok_or_else(|| {
        if value < 0 {
            anyhow::anyhow!("invalid negative Binance {field} timestamp: {value}")
        } else {
            anyhow::anyhow!("Binance {field} timestamp is outside the UnixNanos range: {value}")
        }
    })
}

pub(crate) fn parse_millis_or_init(value: i64, field: &str, ts_init: UnixNanos) -> UnixNanos {
    timestamp_or_init(parse_millis(value, field), ts_init)
}

pub(crate) fn parse_micros_or_init(value: i64, field: &str, ts_init: UnixNanos) -> UnixNanos {
    timestamp_or_init(parse_micros(value, field), ts_init)
}

fn timestamp_or_init(timestamp: anyhow::Result<UnixNanos>, ts_init: UnixNanos) -> UnixNanos {
    match timestamp {
        Ok(timestamp) => timestamp,
        Err(e) => {

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Identify the offending field from the {field} placeholder, then inspect the raw Binance payload - -1 means 'absent', not a valid time
  2. Update the adapter to a version handling the sentinel for that field, or report the field to maintainers
  3. If replaying old captures, regenerate/normalize them so absent times are omitted rather than -1

Example fix

// before: strict parse of a sentinel-carrying field
let ts = parse_millis(raw_time, "eventTime")?;

// after: treat the -1 sentinel as absent
let ts = if raw_time < 0 { fallback_ts } else { parse_millis(raw_time, "eventTime")? };
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_representable_timestamp(value: i64) -> bool {
    value >= 0
}

Type guard

fn is_non_negative_timestamp(value: i64) -> bool {
    value >= 0
}

Try / catch

let ts = parse_millis(value, field).unwrap_or_else(|e| {
    log::warn!("invalid {field} timestamp {value} ({e}) - using event ts_init fallback");
    ts_init
});

Prevention

When it happens

Trigger: A REST/stream payload contains a negative value (typically -1) in a timestamp field the adapter parses strictly via parse_millis/parse_micros - e.g. an optional event time absent for a newly listed or partially populated instrument.

Common situations: Replaying captured data whose sentinel fields were not normalized; a Binance endpoint starting to populate an optional time with -1; upstream schema drift on specific symbols.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/7b3f24927b0a5392. Report an issue: GitHub.