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

  1. Fix the system clock and enable NTP (timedatectl set-ntp true on Linux, w32tm /resync on Windows)
  2. In VMs, sync guest time to the host via hypervisor tools before running GitButler operations
  3. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/e1b6ff42c43c3fa4. Report an issue: GitHub.