{"record":{"id":"b7117cb7988cd966","repo":"nautechsystems/nautilus_trader","slug":"negative-timestamp-not-allowed","errorCode":null,"errorMessage":"Negative timestamp not allowed","messagePattern":"Negative timestamp not allowed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/datetime.rs","lineNumber":582,"sourceCode":"///\n/// Returns an error if the resulting date would be invalid or out of range.\npub fn add_n_months(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {\n    shift_months(datetime, i64::from(n))\n        .map_err(|_| anyhow::anyhow!(\"Failed to add {n} months to {datetime}\"))\n}\n\n/// Subtract `n` months from a given UNIX nanoseconds timestamp.\n///\n/// # Errors\n///\n/// Returns an error if the resulting timestamp is out of range or invalid.\npub fn subtract_n_months_nanos(unix_nanos: UnixNanos, n: u32) -> anyhow::Result<UnixNanos> {\n    let datetime = unix_nanos.to_datetime_utc();\n    let result = subtract_n_months(datetime, n)?;\n    let timestamp = result.as_nanosecond();\n\n    let nanos =\n        u64::try_from(timestamp).map_err(|_| anyhow::anyhow!(\"Negative timestamp not allowed\"))?;\n    Ok(UnixNanos::from(nanos))\n}\n\n/// Add `n` months to a given UNIX nanoseconds timestamp.\n///\n/// # Errors\n///\n/// Returns an error if the resulting timestamp is out of range or invalid.\npub fn add_n_months_nanos(unix_nanos: UnixNanos, n: u32) -> anyhow::Result<UnixNanos> {\n    let datetime = unix_nanos.to_datetime_utc();\n    let result = add_n_months(datetime, n)?;\n    let timestamp = result.as_nanosecond();\n\n    let nanos =\n        u64::try_from(timestamp).map_err(|_| anyhow::anyhow!(\"Negative timestamp not allowed\"))?;\n    Ok(UnixNanos::from(nanos))\n}\n","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/datetime.rs#L564-L600","documentation":"subtract_n_months_nanos converts the result back to u64 nanoseconds for UnixNanos; \"Negative timestamp not allowed\" means the shifted timestamp is before the UNIX epoch (negative i128/i64 nanos), which the unsigned UnixNanos type cannot hold. The library throws it because nautilus timestamps are non-negative by definition.","triggerScenarios":"Calling subtract_n_months_nanos(ts, n) where ts is within n months after 1970-01-01, e.g. subtract_n_months_nanos(UnixNanos::from(1970-06-15), 12) or any timer start near the epoch with a large lookback; also from start_timer_internal with a too-large n.","commonSituations":"Historical backtests replaying data very close to the epoch; subtracting a lookback interval larger than the elapsed time since 1970; misconfigured lookback months (n) in timers.","solutions":["Check before calling that unix_nanos is large enough: elapsed nanos since epoch >= n months of nanoseconds.","Clamp the result to 0 (epoch) if a pre-epoch floor is acceptable for your logic.","Reduce n or use a different anchoring timestamp further from the epoch.","If pre-epoch values are legitimate for your use case, use jiff Timestamp/i64 arithmetic instead of UnixNanos-based APIs."],"exampleFix":"// before\nlet anchored = subtract_n_months_nanos(unix_nanos, n)?; // may go pre-epoch\n// after\nlet months_ns: u64 = n as u64 * 30 * 24 * 3600 * 1_000_000_000;\nlet anchored = if unix_nanos.as_u64() > months_ns {\n    subtract_n_months_nanos(unix_nanos, n)?\n} else {\n    UnixNanos::from(0u64)\n};","handlingStrategy":"validation","validationCode":"// Rust: ensure the lookback cannot cross the epoch\nconst NS_PER_MONTH: u64 = 30 * 24 * 3600 * 1_000_000_000;\nfn can_subtract(unix_nanos: UnixNanos, n: u32) -> bool {\n    unix_nanos.as_u64() > n as u64 * NS_PER_MONTH\n}","typeGuard":null,"tryCatchPattern":"match subtract_n_months_nanos(unix_nanos, n) {\n    Ok(ns) => ns,\n    Err(_) => UnixNanos::from(0u64), // clamp to epoch as floor\n}","preventionTips":["Compute the elapsed time since epoch and compare with the requested lookback before subtracting.","Clamp to epoch 0 when a pre-epoch floor is acceptable in backtests."],"tags":["datetime","unix-epoch","underflow","rust"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}