{"record":{"id":"6333e93c9c0c3461","repo":"nautechsystems/nautilus_trader","slug":"ax-timestamp-must-be-non-negative-was-seconds","errorCode":null,"errorMessage":"AX timestamp must be non-negative, was {seconds}","messagePattern":"AX timestamp must be non-negative, was (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/common/parse.rs","lineNumber":46,"sourceCode":"};\nuse nautilus_model::{\n    data::BarSpecification,\n    enums::AggressorSide,\n    identifiers::{ClientOrderId, TradeId},\n    types::{Price, Quantity, fixed::FIXED_PRECISION, quantity::QuantityRaw},\n};\n\nuse super::enums::AxCandleWidth;\n\nconst NANOSECONDS_IN_SECOND: u64 = 1_000_000_000;\n\n/// Converts an AX epoch-seconds timestamp to [`UnixNanos`].\n///\n/// # Errors\n///\n/// Returns an error if `seconds` is negative (malformed data from AX).\npub fn ax_timestamp_s_to_unix_nanos(seconds: i64) -> anyhow::Result<UnixNanos> {\n    anyhow::ensure!(\n        seconds >= 0,\n        \"AX timestamp must be non-negative, was {seconds}\"\n    );\n    Ok(UnixNanos::from(seconds as u64 * NANOSECONDS_IN_SECOND))\n}\n\n/// Converts AX `ts` (seconds) + `tn` (nanoseconds) fields to [`UnixNanos`].\n///\n/// # Errors\n///\n/// Returns an error if `seconds` is negative (malformed data from AX).\npub fn ax_timestamp_stn_to_unix_nanos(seconds: i64, nanos: i64) -> anyhow::Result<UnixNanos> {\n    anyhow::ensure!(\n        seconds >= 0,\n        \"AX timestamp must be non-negative, was {seconds}\"\n    );\n    let nanos_part = nanos.max(0) as u64;\n    Ok(UnixNanos::from(","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/architect_ax/src/common/parse.rs#L28-L64","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the raw AX message to find why the timestamp is negative (field shift, sentinel value)","Skip or drop the malformed report and log the offending message","Sanitize/negotiate upstream data; if sentinel -1 is expected, filter it before conversion"],"exampleFix":"// before\nlet ts = ax_timestamp_s_to_unix_nanos(report.ts)?;\n// after\nif report.ts < 0 { log::warn!(\"skipping report with negative ts\"); return Ok(None); }\nlet ts = ax_timestamp_s_to_unix_nanos(report.ts)?;","handlingStrategy":"validation","validationCode":"// Rust\nif seconds < 0 { /* skip message or substitute clock timestamp */ }","typeGuard":null,"tryCatchPattern":"// Rust\nmatch ax_timestamp_s_to_unix_nanos(seconds) {\n    Ok(ts) => ts,\n    Err(e) => { log::warn!(\"malformed AX timestamp: {e}\"); skip(); }\n}","preventionTips":["Validate timestamp fields before conversion when consuming raw AX payloads","Log raw messages with negative timestamps for upstream reporting","Handle sentinel values like -1 explicitly before parsing"],"tags":["timestamp","validation","adapter"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}