nautechsystems/nautilus_trader · error · anyhow::Error
Timestamp out of range for trade {}
Error message
Timestamp out of range for trade {} What it means
parse_trade_tick converts a trade's created_at timestamp into UnixNanos (u64). The error fires when u64::try_from fails because the timestamp is negative (before Unix epoch) or out of u64 nanosecond range, so the trade tick cannot be constructed.
Source
Thrown at crates/adapters/dydx/src/http/parse.rs:84
trade: &Trade,
instrument_id: InstrumentId,
price_precision: u8,
size_precision: u8,
ts_init: UnixNanos,
) -> anyhow::Result<TradeTick> {
let aggressor_side = match trade.side {
OrderSide::Buy => AggressorSide::Buy,
OrderSide::Sell => AggressorSide::Sell,
};
let price = Price::from_decimal_dp(trade.price, price_precision)
.context(format!("failed to parse price for trade {}", trade.id))?;
let size = Quantity::from_decimal_dp(trade.size, size_precision)
.context(format!("failed to parse size for trade {}", trade.id))?;
let ts_event_nanos = u64::try_from(trade.created_at.as_nanosecond())
.map_err(|_| anyhow::anyhow!("Timestamp out of range for trade {}", trade.id))?;
let ts_event = UnixNanos::from(ts_event_nanos);
Ok(TradeTick::new(
instrument_id,
price,
size,
aggressor_side,
TradeId::new(&trade.id),
ts_event,
ts_init,
))
}
/// Parses a dYdX [`Candle`] into a Nautilus [`Bar`].
///
/// When `timestamp_on_close` is true, `ts_event` is set to bar close time
/// (started_at + interval). When false, uses the venue-native open time.
///View on GitHub (pinned to 18893faf8b)
Solutions
- Log the offending trade payload (trade.id and raw created_at) to confirm the actual value
- Fix the DateTime deserialization of created_at if the format/epoch assumption is wrong
- Skip trades with out-of-range timestamps instead of failing the whole batch
- Check for dYdX API version changes affecting the timestamp field
Example fix
// before
let ts_event_nanos = u64::try_from(trade.created_at.as_nanosecond())
.map_err(|_| anyhow::anyhow!("Timestamp out of range for trade {}", trade.id))?;
// after
let ts_event_nanos = u64::try_from(trade.created_at.as_nanosecond())
.map_err(|_| anyhow::anyhow!(
"Timestamp out of range for trade {}: raw created_at={:?}",
trade.id, trade.created_at
))?; Defensive patterns
Strategy: try-catch
Validate before calling
if u64::try_from(trade.created_at.as_nanosecond()).is_err() {
log::warn!("trade {} has out-of-range created_at, skipping", trade.id);
return Ok(None);
} Type guard
fn trade_timestamp_in_range(trade: &DydxTrade) -> bool {
u64::try_from(trade.created_at.as_nanosecond()).is_ok()
} Try / catch
match parse_trade_tick(trade, instrument, price_precision, size_precision) {
Ok(tick) => handle(tick),
Err(e) if e.to_string().contains("Timestamp out of range") => {
log::warn!("skipping trade with bad timestamp: {e}");
}
Err(e) => return Err(e),
} Prevention
- Log raw created_at values when parsing fails to spot format drift
- Validate timestamps at the HTTP deserialization layer, not at tick construction
- Keep parse tests pinned to realistic dYdX trade fixtures
When it happens
Trigger: Parsing a dYdX trade whose created_at is negative or exceeds u64 nanoseconds, via parse_trade_tick (invoked from trade-tick requests).
Common situations: API returning a malformed or zeroed created_at; wrong datetime parsing producing pre-epoch values; upstream schema change in the trade feed; corrupted test fixture data (this symbol is exercised by test_parse_trade_tick).
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Timestamp overflow for {}
- Timestamp out of range for candle at {}
- slots lock poisoned
- UnixNanos overflow in from_seconds
- UnixNanos overflow in from_millis
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b2bd2e81d3fa5eef.
Report an issue: GitHub.