nautechsystems/nautilus_trader · error
system clock is before UNIX epoch
Error message
system clock is before UNIX epoch
What it means
build_auth_token_for timestamps the auth token with the current system time. If SystemTime::now() is before the UNIX epoch — only possible on systems with a clock set earlier than 1970-01-01 — duration_since(UNIX_EPOCH) fails and this error is raised instead of producing a token with a bogus/negative timestamp.
Source
Thrown at crates/adapters/lighter/src/signing/auth_token.rs:121
/// Mint an auth token from a [`crate::common::credential::Credential`] using
/// the default 7-hour TTL and a fresh CSPRNG nonce.
///
/// The token format matches the Go reference's `ConstructAuthToken`. The
/// returned string is the value the WebSocket subscribe handshake sends in
/// the `auth` field of an `account_*` channel subscription.
///
/// # Errors
///
/// Returns the underlying [`crate::common::credential::Credential::private_key`]
/// failure if the secret cannot be decoded, or any [`build_auth_token`]
/// failure (clock-before-epoch or, hypothetically, a breach caused by its own
/// deadline validation).
pub fn build_auth_token_for(
credential: &crate::common::credential::Credential,
) -> anyhow::Result<SecretString> {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|_| anyhow::anyhow!("system clock is before UNIX epoch"))?
.as_secs();
let now_i64 = i64::try_from(now)
.map_err(|_| anyhow::anyhow!("system clock overflowed when converting to i64"))?;
let deadline = now_i64
.checked_add(DEFAULT_AUTH_TOKEN_TTL_SECS)
.ok_or_else(|| anyhow::anyhow!("deadline computation overflowed"))?;
let sk = credential.private_key()?;
build_auth_token(
deadline,
credential.account_index(),
credential.api_key_index(),
&sk,
fresh_k(),
)
.map_err(|e| anyhow::anyhow!("failed to mint Lighter auth token: {e}"))
}
/// Draws a fresh canonical [`Scalar`] from the thread-local CSPRNG suitableView on GitHub (pinned to 18893faf8b)
Solutions
- Synchronize the system clock (NTP: `chronyc makestep`, `ntpdate`, or systemd-timesyncd) and retry.
- Check `date -u` — if it shows a pre-1970 date, fix the OS/hypervisor clock configuration.
- In containers/VMs, restart the host clock sync or resync after suspend/resume.
- Ensure the process is not chrooted into an environment with a fabricated clock.
Example fix
// host shell $ date -u # shows e.g. 1969-12-31 $ sudo systemctl enable --now systemd-timesyncd $ sudo chronyc -a makestep $ date -u # now current; retry the client
Defensive patterns
Strategy: validation
Validate before calling
// Guard before calling authenticated APIs
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)
.map_err(|_| anyhow!("fix system clock before authenticating"))?; Try / catch
match build_auth_token_for(&credential) {
Ok(token) => token,
Err(e) if e.to_string().contains("before UNIX epoch") => {
eprintln!("system clock is wrong; enable NTP and retry");
std::process::exit(1);
}
Err(e) => return Err(e),
} Prevention
- Run NTP/chrony or systemd-timesyncd on hosts running the client.
- Check clock correctness at process startup and fail fast.
- In VMs/containers, ensure host clock sync after suspend/resume.
When it happens
Trigger: Calling any authenticated flow (apply_referral_attribution, is_maker_only_api_key, WS consumer spawn, order status reports, fill report pagination) on a machine whose system clock is set before 1970-01-01.
Common situations: Fresh embedded boards/VMs without RTC and unsynced clocks; containers with wrong system time after host suspend; misconfigured timezones/time manually set far in the past.
Related errors
- system clock overflowed when converting to i64
- DataActor {} must be registered before calling `clock()` - t
- DataActor {} must be registered before calling `clock_ref()`
- Event '{}' should have associated handler
- Timer '{name}' alert time {} was in the past (current time i
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/96aa33f362dbb819.
Report an issue: GitHub.