nautechsystems/nautilus_trader · error · anyhow::Error
AX timestamp_ns must be non-negative, was {nanos}
Error message
AX timestamp_ns must be non-negative, was {nanos} What it means
The ns-flavour guard: ax_timestamp_ns_to_unix_nanos takes a single nanosecond timestamp and rejects negative values as malformed venue data (UnixNanos is a u64-backed type, so a negative i64 would wrap catastrophically if cast blindly). The message names the field style ('timestamp_ns') to distinguish it from the seconds-based guards.
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 a4b06ed870)
Solutions
- Report the raw message to maintainers with the decoded field value
- Replace negative sentinels in recorded/test data with valid ns-since-epoch values
- Upgrade the architect_ax adapter to the latest release matching the venue API
Example fix
// before (fixture) let ts = ax_timestamp_ns_to_unix_nanos(-1)?; // AX timestamp_ns must be non-negative, was -1 // after let ts = ax_timestamp_ns_to_unix_nanos(1_700_000_000_000_000_000)?;
Defensive patterns
Strategy: try-catch
Type guard
fn is_valid_ax_nanos(nanos: i64) -> bool {
nanos >= 0
} Try / catch
match ax_timestamp_ns_to_unix_nanos(ts_ns) {
Ok(nanos) => nanos,
Err(e) => {
tracing::error!("dropping event with negative ns timestamp {ts_ns}: {e}");
return Ok(());
}
} Prevention
- Never use -1 (or other negatives) as timestamp sentinels in AX test data
- Alert on this error in production: it signals upstream data corruption, not user error
When it happens
Trigger: An inbound AX message whose nanosecond timestamp field decodes negative — corrupt frame, protocol drift after an Architect API change, or fixture data using -1 sentinels.
Common situations: Adapter-level data integrity issue rather than a user error; encountered when testing against recorded venue data with placeholder values, or during an upstream protocol change.
Related errors
- AX timestamp must be non-negative, was {seconds}
- Unsupported bar specification for AX: {step}-{:?}
- AX requires whole contract quantities, was {}
- Order quantity must be at least 1 contract
- pandas is required for report generation; install it with `p
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/b7fcd4ee30bf5880.
Report an issue: GitHub.