nautechsystems/nautilus_trader · warning
FX session date must be representable
Error message
FX session date must be representable
What it means
In `fx_next_boundary`, when the current local time is past the session time, the code advances the date by one day via `checked_add` and panics if the resulting date is not representable. `jiff`'s `CivilDate` has bounded min/max years, so this only fires at the extreme edge of the representable calendar range. In practice it is an unreachable defensive panic for normal timestamps.
Source
Thrown at crates/trading/src/sessions.rs:142
}
/// Returns the previous session end time in UTC.
#[must_use]
pub fn fx_prev_end(session: ForexSession, time_now: Timestamp) -> Timestamp {
let local_now = fx_local_from_utc(session, time_now);
let (_, end_time) = session.session_times();
fx_prev_boundary(&local_now, end_time)
}
fn fx_next_boundary(local_now: &Zoned, session_time: Time) -> Timestamp {
let timezone = local_now.time_zone().clone();
let mut date = local_now.date();
if local_now.time() > session_time {
date = date
.checked_add(Span::new().days(1))
.expect("FX session date must be representable");
}
let weekend_days = match date.weekday() {
Weekday::Saturday => 2,
Weekday::Sunday => 1,
_ => 0,
};
date = date
.checked_add(Span::new().days(weekend_days))
.expect("FX session date must be representable");
timezone
.to_ambiguous_timestamp(date.to_datetime(session_time))
.unambiguous()
.expect("FX session boundary must be a unique local time")
}
fn fx_prev_boundary(local_now: &Zoned, session_time: Time) -> Timestamp {View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the system clock and any injected timestamps are within realistic (jiff-representable, well-below year 9999) ranges.
- Clamp or validate `local_now` before calling session helpers if timestamps come from external/untrusted data.
- If you legitimately operate near the boundary, compute the boundary with `checked_add` and handle `None` yourself instead of the panicking helper.
- Report upstream if reachable with a sane clock — it indicates an unexpected input path.
Example fix
// before (caller)
let next = fx_next_start(ForexSession::London, &now); // panics on overflow
// after (defensive pre-check)
let now = Zoned::now();
assert!(now.year() < 9999, "system clock far out of range: {}", now.year());
let next = fx_next_start(ForexSession::London, &now); Defensive patterns
Strategy: validation
Validate before calling
// Reject absurd timestamps before calling session helpers
fn timestamp_in_reasonable_range(now: &jiff::Zoned) -> bool {
(-2200..=2200).contains(&now.year())
} Try / catch
// Rust panics are not catchable; pre-validate instead let now = jiff::Zoned::now(); assert!(timestamp_in_reasonable_range(&now), "clock outside sane range");
Prevention
- Sanity-check the system clock (NTP) — extreme dates only occur with broken clocks or mocks.
- When fuzzing/testing with synthetic Zoned values, keep years within a realistic window.
- Remember jiff CivilDate bounds (roughly -9999..=9999) and stay far from them.
When it happens
Trigger: Calling `fx_next_start`/`fx_next_end` (via `fx_next_boundary`) while the current local date is at `CivilDate`'s maximum (year 9999) and the local time is past the session time, forcing a +1 day overflow.
Common situations: Only with fabricated/system clock values at the far end of the supported date range; mocked `Zoned` inputs in tests or corrupted clock data far beyond realistic trading dates.
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
- in-flight mutex poisoned
- wallet balance mutex poisoned
- instrument update lock poisoned
- rate limiter decision lock poisoned
- Unsupported blockchain {blockchain} for RPC connection
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e2ffd475fa8b1440.
Report an issue: GitHub.