astrid-runtime/astrid · error

WinFsp lease is expired

Error message

WinFsp lease is expired

What it means

Each mount lease carries an absolute expiry (expires_at_epoch_secs, Unix seconds). validate_service_launch compares it to the current system clock and aborts the mount if the lease has already expired. This enforces that only time-valid leases can bring up a WinFsp filesystem, preventing stale mounts from being resurrected.

Source

Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:263

        || launch.parent.token.chars().any(char::is_control)
    {
        bail!("WinFsp service parent token is invalid");
    }
    if let Some(identity) = launch.parent.start_identity.as_deref()
        && (identity.is_empty() || identity.len() > 512 || identity.chars().any(char::is_control))
    {
        bail!("WinFsp service parent start identity is invalid");
    }
    if launch.parent.start_identity.is_none() {
        bail!("WinFsp service parent start identity is required on Windows");
    }
    let lease = &launch.lease;
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .context("read system clock")?
        .as_secs();
    if lease.expires_at_epoch_secs < now {
        bail!("WinFsp lease is expired");
    }
    if lease.lease_token.len() < 16 || lease.lease_token.len() > 4096 {
        bail!("WinFsp lease callback token is invalid");
    }
    if !lease.resource_path.is_absolute()
        || !lease.callback_path.is_absolute()
        || lease.callback_path != lease.resource_path.join("control.endpoint")
    {
        bail!("WinFsp lease paths are malformed");
    }
    platform_fs::validate_private_directory(&lease.resource_path)
        .context("validate private WinFsp lease resource")?;
    platform_fs::verify_no_redirects(&lease.resource_path)
        .context("reject redirected WinFsp lease resource")?;
    let manifest_path = lease.resource_path.join("lease.json");
    platform_fs::validate_private_file(&manifest_path)
        .context("validate private WinFsp lease manifest")?;
    let manifest = std::fs::read(&manifest_path).context("read WinFsp lease manifest")?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Obtain a fresh lease (re-run the mount request through the provider) so expires_at_epoch_secs is in the future, instead of replaying an old launch descriptor.
  2. Check the system clock on the service host (w32tm /query /status; re-sync with time.windows.com) — a forward clock skew alone produces this error with a valid lease.
  3. Re-mint the launch file right before starting the service; avoid persisting launch descriptors across restarts or long waits.
  4. If leases expire during legitimate startup delays, increase the lease TTL in the code that issues StorageMountLeaseV1.
Defensive patterns

Strategy: validation

Validate before calling

let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs();
if lease.expires_at_epoch_secs <= now {
    return Err(anyhow!("lease expired at {} (now {}): re-issue the lease", lease.expires_at_epoch_secs, now));
}

Try / catch

match validate_launch(&launch) {
    Err(e) if e.to_string().contains("lease is expired") => reissue_lease_and_retry(),
    Err(e) => return Err(e),
    Ok(()) => start_service(&launch),
}

Prevention

When it happens

Trigger: service_main -> validate_service_launch where lease.expires_at_epoch_secs < SystemTime::now() duration since UNIX_EPOCH in seconds: the lease file (lease.json) was created earlier and its validity window has passed before/at service start.

Common situations: Resuming a session or replaying a saved launch descriptor hours later; a machine with a wrong system clock (skewed forward, or timezone/clock jumps after resume from sleep or VM snapshot) makes a fresh lease look expired; long CI queues where the lease was minted at queue time but consumed late.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/52a5702b5dbbb5f6. Report an issue: GitHub.