nautechsystems/nautilus_trader · error

Failed to parse ISO 8601 string '{date_string}': {e}

Error message

Failed to parse ISO 8601 string '{date_string}': {e}

What it means

iso8601_to_unix_nanos delegates to `str::parse::<UnixNanos>`, and when parsing fails it wraps the underlying parse error with the offending string. It means the input string was not recognized as a valid ISO 8601 / RFC 3339 timestamp (or was out of range for UnixNanos). The library throws it because a free-form string cannot be safely converted to a nanosecond UNIX timestamp without strict validation.

Source

Thrown at crates/core/src/datetime.rs:454

///
/// - `date_string`: The ISO 8601 formatted date string to parse
///
/// # Returns
///
/// Returns `Ok(UnixNanos)` if the string is successfully parsed, or an error if the format
/// is invalid or the timestamp is out of range.
///
/// # Errors
///
/// Returns an error if:
/// - The string format is not a valid ISO 8601 format
/// - The timestamp is out of range for `UnixNanos`
/// - The date/time values are invalid
#[inline]
pub fn iso8601_to_unix_nanos(date_string: &str) -> anyhow::Result<UnixNanos> {
    date_string
        .parse::<UnixNanos>()
        .map_err(|e| anyhow::anyhow!("Failed to parse ISO 8601 string '{date_string}': {e}"))
}

/// Converts a UNIX nanoseconds timestamp to an ISO 8601 (RFC 3339) format string
/// with millisecond precision.
///
/// All [`UnixNanos`] values are representable by this formatter.
#[inline]
#[must_use]
pub fn unix_nanos_to_iso8601_millis(unix_nanos: UnixNanos) -> String {
    let parts = split_unix_nanos(unix_nanos);

    let mut out = String::with_capacity(24);
    push_iso8601_prefix(
        &mut out,
        parts.year,
        parts.month,
        parts.day,
        parts.hour,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate and normalize the string to RFC 3339 before calling, e.g. ensure a 'T' separator and a 'Z'/offset suffix.
  2. Print the inner parse error (it is included in the message) to see exactly which part of the format jiff rejected.
  3. If the value is a plain epoch number, parse it as an integer and construct UnixNanos directly instead of ISO parsing.
  4. For pre-1970 timestamps, switch to a signed representation at the call site; UnixNanos is u64 and cannot hold them.

Example fix

// before
let ns = iso8601_to_unix_nanos("2024-01-01 00:00:00")?; // no offset -> parse error
// after
let ns = iso8601_to_unix_nanos("2024-01-01T00:00:00Z")?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate RFC 3339 shape before calling
fn is_rfc3339(s: &str) -> bool {
    s.len() >= 20 && s.as_bytes()[10] == b'T' && (s.ends_with('Z') || s.contains('+') || s.contains('-'))
}
if !is_rfc3339(date_string) { /* normalize or reject */ }

Try / catch

match iso8601_to_unix_nanos(s) {
    Ok(ns) => ns,
    Err(e) => { log::warn!("bad timestamp {s}: {e}"); return Err(e); }
}

Prevention

When it happens

Trigger: Calling iso8601_to_unix_nanos (or iso_to_unix_nanos) with a malformed string: missing timezone (e.g. "2024-01-01 00:00:00" without offset), wrong separator (space instead of 'T' when required), fractional precision beyond supported format, empty string, or a timestamp outside the u64-nanosecond range (pre-1970 dates).

Common situations: Feeding user-supplied or config-file datetimes straight in; log timestamps in non-RFC3339 formats; Python callers passing datetime.strftime output without timezone; data from exchanges using "YYYYMMDD" or epoch-millis strings; negative/pre-epoch dates that overflow UnixNanos (u64).

Understand the failure class

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/4870858f66211fdf. Report an issue: GitHub.