nautechsystems/nautilus_trader · error
Trusted host clock precedes the Unix epoch
Error message
Trusted host clock precedes the Unix epoch
What it means
`current_unix_secs` reads the host clock via `SystemTime::now().duration_since(UNIX_EPOCH)` and fails if the system time is before the Unix epoch. The client trusts the host clock for expiry/scan computations, so a pre-epoch clock is treated as a fatal environment error rather than producing a negative/underflowing timestamp.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:3585
fn release_slot(&self) {
*self.in_flight.lock() = None;
}
}
fn replacement_scan_range(from_block: u64, head_block: u64) -> anyhow::Result<RangeInclusive<u64>> {
anyhow::ensure!(
head_block >= from_block,
"Canonical head {head_block} is behind execution creation block {from_block}"
);
let max_end = from_block.saturating_add(MAX_REPLACEMENT_SCAN_BLOCKS - 1);
Ok(from_block..=head_block.min(max_end))
}
fn current_unix_secs() -> anyhow::Result<u64> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|_| anyhow::anyhow!("Trusted host clock precedes the Unix epoch"))
.map(|duration| duration.as_secs())
}
fn validate_payload_operation_batch_size(batch_size: usize) -> anyhow::Result<i64> {
anyhow::ensure!(
(1..=MAX_PAYLOAD_OPERATION_BATCH_SIZE).contains(&batch_size),
"Payload operation batch size must be between 1 and {MAX_PAYLOAD_OPERATION_BATCH_SIZE}"
);
Ok(i64::try_from(batch_size).expect("bounded payload batch size fits i64"))
}
fn current_execution_hash(
intent_id: i64,
hashes: &[ExecutionTransactionHashRow],
) -> anyhow::Result<&ExecutionTransactionHashRow> {
let mut current = hashes.iter().filter(|row| row.current);
let row = current
.next()View on GitHub (pinned to 18893faf8b)
Solutions
- Fix the host clock: run NTP sync (`timedatectl set-ntp true`, `chronyc makestep`).
- Manually set the date (`date -s @<epoch>`) if NTP is unavailable.
- Add clock sanity checks to startup health checks before launching the trader.
Example fix
// before: host clock at 1969 SystemTime::now().duration_since(UNIX_EPOCH) -> Err // after: sync host $ sudo timedatectl set-ntp true && timedatectl status
Defensive patterns
Strategy: validation
Validate before calling
let now = SystemTime::now().duration_since(UNIX_EPOCH)
.map_err(|_| anyhow::anyhow!("host clock invalid; run NTP sync before starting"))?;
anyhow::ensure!(now.as_secs() > 1_600_000_000, "host clock implausibly old"); Try / catch
match current_unix_secs() {
Err(e) if e.to_string().contains("precedes the Unix epoch") => {
sync_host_clock()?;
current_unix_secs()
}
other => other,
} Prevention
- Enable NTP on all hosts running the trader
- Add a clock sanity check to startup health checks
- Avoid VMs/images without RTC or configured time sources
When it happens
Trigger: Any code path calling `current_unix_secs()` (e.g. replacement scans, expiry checks) on a host whose wall clock is set before 1970-01-01 — typically a VM without RTC after power loss, or an embedded/board with uninitialized clock.
Common situations: Freshly provisioned VPS/containers with unset system time; Raspberry Pi or IoT boards without battery-backed RTC; misconfigured NTP; test VMs with frozen clocks.
Understand the failure class
Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.
Related errors
- system clock before Unix epoch: {e}
- Binance US supports the Live environment only
- Time went backwards
- Invalid config type for AxExecutionClientFactory. Expected A
- Invalid config type for BetfairDataClientFactory. Expected B
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7215ffe3bc768524.
Report an issue: GitHub.