nautechsystems/nautilus_trader · error

AX timestamp must be non-negative, was

Error message

AX timestamp must be non-negative, was {seconds}

What it means

ax_timestamp_s_to_unix_nanos converts AX epoch-seconds (i64) to UnixNanos (u64). Negative seconds indicate malformed data from the AX feed, so the function errors via anyhow::ensure before the cast. This protects the unsigned UnixNanos type from wrap-around.

Solutions

  1. Inspect the raw AX message to find why the timestamp is negative (field shift, sentinel value)
  2. Skip or drop the malformed report and log the offending message
  3. Sanitize/negotiate upstream data; if sentinel -1 is expected, filter it before conversion

Example fix

// before
let ts = ax_timestamp_s_to_unix_nanos(report.ts)?;
// after
if report.ts < 0 { log::warn!("skipping report with negative ts"); return Ok(None); }
let ts = ax_timestamp_s_to_unix_nanos(report.ts)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust
if seconds < 0 { /* skip message or substitute clock timestamp */ }

Try / catch

// Rust
match ax_timestamp_s_to_unix_nanos(seconds) {
    Ok(ts) => ts,
    Err(e) => { log::warn!("malformed AX timestamp: {e}"); skip(); }
}

Prevention

When it happens

Trigger: Calling ax_timestamp_s_to_unix_nanos with a negative i64, typically from parse_bar or parse_order_status_report_fields when AX sends a malformed/negative timestamp field.

Common situations: AX upstream sending corrupt or placeholder timestamps (e.g. -1 sentinels); misparsed fields shifting a signed value into the timestamp column; unit tests feeding negative values.

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

Appendix: source

Thrown at crates/adapters/architect_ax/src/common/parse.rs:46

};
use nautilus_model::{
    data::BarSpecification,
    enums::AggressorSide,
    identifiers::{ClientOrderId, TradeId},
    types::{Price, Quantity, fixed::FIXED_PRECISION, quantity::QuantityRaw},
};

use super::enums::AxCandleWidth;

const NANOSECONDS_IN_SECOND: u64 = 1_000_000_000;

/// Converts an AX epoch-seconds timestamp to [`UnixNanos`].
///
/// # Errors
///
/// Returns an error if `seconds` is negative (malformed data from AX).
pub fn ax_timestamp_s_to_unix_nanos(seconds: i64) -> anyhow::Result<UnixNanos> {
    anyhow::ensure!(
        seconds >= 0,
        "AX timestamp must be non-negative, was {seconds}"
    );
    Ok(UnixNanos::from(seconds as u64 * NANOSECONDS_IN_SECOND))
}

/// Converts AX `ts` (seconds) + `tn` (nanoseconds) fields to [`UnixNanos`].
///
/// # Errors
///
/// Returns an error if `seconds` is negative (malformed data from AX).
pub fn ax_timestamp_stn_to_unix_nanos(seconds: i64, nanos: i64) -> anyhow::Result<UnixNanos> {
    anyhow::ensure!(
        seconds >= 0,
        "AX timestamp must be non-negative, was {seconds}"
    );
    let nanos_part = nanos.max(0) as u64;
    Ok(UnixNanos::from(

View on GitHub (pinned to 18893faf8b)