nautechsystems/nautilus_trader · error · anyhow::Error

Invalid timestamp '{ts}': {e}

Error message

Invalid timestamp '{ts}': {e}

What it means

parse_timestamp_ms converts a millisecond epoch string from Polymarket messages into a UnixNanos value. If the string cannot be parsed as a u64 (non-numeric, empty, or containing invalid characters), the function wraps the std parse error in an anyhow error with the offending input.

Source

Thrown at crates/adapters/polymarket/src/websocket/parse.rs:47

    types::{Price, Quantity},
};
use rust_decimal::Decimal;
use serde::Serialize;

use super::messages::{
    PolymarketBestBidAsk, PolymarketBookLevel, PolymarketBookSnapshot, PolymarketQuote,
    PolymarketTrade,
};
use crate::common::{
    enums::PolymarketOrderSide,
    parse::{determine_trade_id, parse_decimal_exact},
};

/// Parses a millisecond epoch timestamp string into [`UnixNanos`].
pub fn parse_timestamp_ms(ts: &str) -> anyhow::Result<UnixNanos> {
    let ms: u64 = ts
        .parse()
        .map_err(|e| anyhow::anyhow!("Invalid timestamp '{ts}': {e}"))?;
    let ns = ms
        .checked_mul(NANOSECONDS_IN_MILLISECOND)
        .ok_or_else(|| anyhow::anyhow!("Timestamp overflow for '{ts}'"))?;
    Ok(UnixNanos::from(ns))
}

pub(crate) fn parse_price(s: &str, precision: u8) -> CorrectnessResult<Price> {
    let value = parse_decimal_exact(s).map_err(|e| CorrectnessError::PredicateViolation {
        message: format!("Invalid price '{s}': {e}"),
    })?;
    Price::from_decimal_dp(value, precision)
}

pub(crate) fn parse_quantity(s: &str, precision: u8) -> CorrectnessResult<Quantity> {
    let value = parse_decimal_exact(s).map_err(|e| CorrectnessError::PredicateViolation {
        message: format!("Invalid quantity '{s}': {e}"),
    })?;
    Quantity::from_decimal_dp(value, precision)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log/inspect the ts string from the error message — it names the exact invalid value
  2. Convert ISO-8601 timestamps to epoch milliseconds before calling (e.g. via chrono)
  3. Truncate or reject float timestamps: parse as f64 then cast, or strip fractional part
  4. Update the adapter parsing if Polymarket changed the field format

Example fix

// before: parse_timestamp_ms("2024-01-15T10:00:00Z") fails
let ts = parse_timestamp_ms(msg.timestamp)?;
// after: normalize ISO timestamps to epoch millis first
let ms = if msg.timestamp.contains('-') {
    chrono::DateTime::parse_from_rfc3339(&msg.timestamp)
        .map_err(|e| anyhow::anyhow!("bad ts: {e}"))?
        .timestamp_millis()
        .to_string()
} else {
    msg.timestamp.clone()
};
let ts = parse_timestamp_ms(&ms)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_epoch_millis(s: &str) -> bool {
    !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit()) && s.len() <= 13
}

Type guard

fn as_epoch_millis(raw: &str) -> Option<u64> { raw.parse::<u64>().ok() }

Try / catch

match parse_timestamp_ms(ts) {
    Ok(nanos) => /* proceed */,
    Err(e) if e.to_string().contains("Invalid timestamp") => {
        log::warn!("dropping message with bad timestamp: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling parse_timestamp_ms with a string that is not a valid u64: empty string, "2024-01-15T10:00:00Z" (ISO format), "1699999999999.5" (float), or a field that is null/missing stringified.

Common situations: Polymarket changing the timestamp field format; passing an ISO-8601 datetime instead of epoch millis; unit tests feeding mock payloads with wrong timestamp encodings.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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