nautechsystems/nautilus_trader · error
AX timestamp_ns must be non-negative, was
Error message
AX timestamp_ns must be non-negative, was {nanos} What it means
ax_timestamp_ns_to_unix_nanos converts an AX nanosecond timestamp (i64) to the unsigned UnixNanos type. Negative nanosecond values indicate malformed AX data, so the function errors before casting to u64, which would otherwise wrap around.
Solutions
- Inspect the AX funding rate message to find the source of the negative timestamp
- Skip the malformed update and log the raw payload
- Filter known sentinel values (e.g. -1) before conversion
Example fix
// before
let ts = ax_timestamp_ns_to_unix_nanos(fr.ts_ns)?;
// after
if fr.ts_ns < 0 { log::warn!("bad funding ts"); return Ok(None); }
let ts = ax_timestamp_ns_to_unix_nanos(fr.ts_ns)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust
if nanos < 0 { /* skip update */ } Try / catch
// Rust
let ts = ax_timestamp_ns_to_unix_nanos(n)
.map_err(|e| { log::warn!("bad AX ts_ns: {e}"); fallback_ts() })?; Prevention
- Sanitize funding rate payloads before conversion
- Filter known AX sentinel negative timestamps
- Alert on repeated negative timestamps — it usually indicates an upstream schema change
When it happens
Trigger: Calling ax_timestamp_ns_to_unix_nanos with a negative i64, typically from parse_funding_rate when the AX funding rate payload carries a negative timestamp.
Common situations: AX sending sentinel/corrupt timestamps in funding rate updates; schema or field-shift issues in the upstream payload; tests exercising the negative-input path.
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
- AX timestamp must be non-negative, was
- AX requires whole contract quantities, was
- Empty underlying for
- event_slug_builder offset resolves before the Unix epoch
- Execution timestamp ' ' was before Unix epoch
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b7fcd4ee30bf5880.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/architect_ax/src/common/parse.rs:75
/// 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(
seconds as u64 * NANOSECONDS_IN_SECOND + nanos_part,
))
}
/// Converts an AX nanosecond timestamp to [`UnixNanos`].
///
/// # Errors
///
/// Returns an error if `nanos` is negative (malformed data from AX).
pub fn ax_timestamp_ns_to_unix_nanos(nanos: i64) -> anyhow::Result<UnixNanos> {
anyhow::ensure!(
nanos >= 0,
"AX timestamp_ns must be non-negative, was {nanos}"
);
Ok(UnixNanos::from(nanos as u64))
}
/// Domain separator for the market-data trade identity digest.
///
/// Changing this invalidates every AX `TradeId` already published or persisted, so bump the
/// version suffix only as a deliberate decision.
const TRADE_ID_DOMAIN: &[u8] = b"nautilus-architect-ax/trade-id/v1";
/// Creates a [`TradeId`] for an AX market-data trade.
///
/// AX publishes no trade identifier for market data: `GET /trades` and the market-data WebSocket
/// both carry only `ts`, `tn`, `s`, `p`, `q`, and `d`, and `tn` is the nanosecond component of the
/// timestamp rather than a sequence number. The composed timestamp alone is not unique either,
/// because one aggressor sweeping several levels reports multiple prints at an identical `ts` andView on GitHub (pinned to 18893faf8b)