gitbutlerapp/gitbutler · warning
system time is set before the Unix epoch
Error message
system time is set before the Unix epoch
What it means
now_ms() (gitbutler-git/src/context.rs:332) timestamps push/fetch activity via UNIX_EPOCH.elapsed(). SystemTime::elapsed returns Err when the current wall clock is before 1970-01-01, and the expect turns that into a panic. Everything around it is fallible, so this fires solely on a machine whose clock reads pre-epoch.
Source
Thrown at crates/gitbutler-git/src/context.rs:335
.serialize(serializer)
}
fn remote_tracking_branch_parts(
repo: &gix::Repository,
branch: &gix::refs::FullNameRef,
) -> Result<(String, String)> {
let (remote, short_name) = extract_remote_name_and_short_name(branch, &repo.remote_names())
.ok_or_else(|| anyhow!("failed to determine remote and branch name for `{branch}`"))?;
let short_name = std::str::from_utf8(short_name.as_ref())
.context(format!("branch name for `{branch}` is not valid UTF-8"))?
.to_owned();
Ok((remote, short_name))
}
fn now_ms() -> u128 {
UNIX_EPOCH
.elapsed()
.expect("system time is set before the Unix epoch")
.as_millis()
}
async fn handle_git_prompt_push(
prompt: String,
askpass: Option<Option<StackId>>,
) -> Option<String> {
if let Some(branch_id) = askpass {
tracing::info!("received prompt for branch push {branch_id:?}: {prompt:?}");
askpass::get_broker()
.expect("askpass broker must be initialized")
.submit_prompt(prompt, askpass::Context::Push { branch_id })
.await
} else {
tracing::warn!("received askpass push prompt but no broker was supplied; returning None");
None
}
}View on GitHub (pinned to caf1f223d3)
Solutions
- Fix the system clock and enable NTP (timedatectl set-ntp true on Linux, w32tm /resync on Windows)
- In VMs, sync guest time to the host via hypervisor tools before running GitButler operations
- If you own the call site, degrade gracefully with unwrap_or_default() instead of panicking (see exampleFix)
Example fix
// before
UNIX_EPOCH.elapsed().expect("system time is set before the Unix epoch").as_millis()
// after
UNIX_EPOCH.elapsed().map(|d| d.as_millis()).unwrap_or(0) Defensive patterns
Strategy: fallback
Validate before calling
// Before running git network ops, sanity-check the clock
if std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.is_err()
{
// refuse to start or force a time sync before pushing/fetching
} Try / catch
// Degrade gracefully: a bad clock should yield 0, not a crash let ms = UNIX_EPOCH.elapsed().map(|d| d.as_millis()).unwrap_or(0);
Prevention
- Enable NTP on machines and VMs that run GitButler operations
- Sync guest clocks after VM snapshot restores
- Avoid time-mocking frameworks that set pre-epoch timestamps around network code
When it happens
Trigger: A push or fetch through gitbutler-git hitting now_ms() while the OS clock is set before the Unix epoch: dead CMOS battery booting to 1970, a VM restored from a snapshot with a broken RTC, or CI/containers with mocked or unset time.
Common situations: Laptops with dead clock batteries, VMs after host suspend/restore without guest-tools time sync, time-mocking test frameworks that install fixed old timestamps, fresh embedded devices without RTC/NTP.
Related errors
- broker already configured
- broker has not been configured
- BUG: {id} is queued
- BUG: It should not be possible to omit sources
- prefiltered
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/e1b6ff42c43c3fa4.
Report an issue: GitHub.