astrid-runtime/astrid · error

WinFsp stop deadline overflow

Error message

WinFsp stop deadline overflow

What it means

Raised in `stop_daemon` (crates/astrid-storage-provider-winfsp/src/win.rs:672) when `Instant::checked_add(DAEMON_STOP_TIMEOUT, now)` returns `None`, i.e. computing the post-stop polling deadline overflows the `tokio::time::Instant` range. This is effectively unreachable in practice (it requires a near-max timestamp) and is a defensive guard so deadline arithmetic never silently wraps into a poll loop that would spin until the 'endpoint remained live' bail.

Source

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

            .context("send WinFsp stop")?;
        stream.flush().await.context("flush WinFsp stop")?;
        let mut acknowledgement = [0_u8; 1];
        stream
            .read_exact(&mut acknowledgement)
            .await
            .context("read WinFsp stop acknowledgement")?;
        if acknowledgement[0] != b'S' {
            bail!("WinFsp daemon returned an invalid stop acknowledgement");
        }
        Result::<()>::Ok(())
    };
    tokio::time::timeout(DAEMON_STOP_TIMEOUT, stop)
        .await
        .map_err(|_| anyhow::anyhow!("WinFsp stop timed out"))??;

    let deadline = tokio::time::Instant::now()
        .checked_add(DAEMON_STOP_TIMEOUT)
        .ok_or_else(|| anyhow::anyhow!("WinFsp stop deadline overflow"))?;
    while endpoint_is_present(control_path) {
        if tokio::time::Instant::now() >= deadline {
            bail!("WinFsp control endpoint remained live after stop");
        }
        tokio::time::sleep(Duration::from_millis(25)).await;
    }
    Ok(())
}

fn initialize_winfsp() -> Result<()> {
    load_adjacent_winfsp().context("load co-installed WinFsp runtime")?;
    winfsp_wrs::init().context("initialize installed WinFsp runtime")
}

fn load_adjacent_winfsp() -> Result<()> {
    let Some(directory) = std::env::current_exe()
        .ok()
        .and_then(|path| path.parent().map(Path::to_path_buf))

View on GitHub (pinned to affd8760f4)

Solutions

  1. Treat it as an environmental clock anomaly: reboot the host or fix the monotonic clock source.
  2. In tests, avoid advancing tokio time to near-`Instant` saturation before calling `stop_daemon`.
  3. If it recurs on real hardware, update the tokio runtime; file a bug including the runtime/clock details.
  4. As a workaround, compute the deadline with `saturating_add` semantics by clamping the timeout.

Example fix

// before
let deadline = tokio::time::Instant::now()
    .checked_add(DAEMON_STOP_TIMEOUT)
    .ok_or_else(|| anyhow::anyhow!("WinFsp stop deadline overflow"))?;
// after (saturating alternative)
let deadline = tokio::time::Instant::now()
    .checked_add(DAEMON_STOP_TIMEOUT)
    .unwrap_or_else(tokio::time::Instant::now);
let deadline = deadline + Duration::from_millis(1);
Defensive patterns

Strategy: fallback

Validate before calling

// Only relevant in tests using paused tokio time:
assert!(tokio::time::Instant::now().checked_add(DAEMON_STOP_TIMEOUT).is_some());

Try / catch

let deadline = tokio::time::Instant::now()
    .checked_add(DAEMON_STOP_TIMEOUT)
    .ok_or_else(|| anyhow::anyhow!("WinFsp stop deadline overflow"))?;
// callers: treat as environment failure and abort teardown with diagnostics
match stop_daemon(&control_path).await {
    Err(e) if e.to_string().contains("deadline overflow") => {
        error!("clock anomaly detected; reboot/fix monotonic clock");
    },
    other => { other?; }
}

Prevention

When it happens

Trigger: Specifically: `tokio::time::Instant::now().checked_add(DAEMON_STOP_TIMEOUT)` yields `None`. This requires the current monotonic instant plus `DAEMON_STOP_TIMEOUT` to exceed the internal representable maximum — an extreme/edge runtime-clock situation, not a user-fixable configuration issue.

Common situations: Essentially never seen by end users; it could only appear with a pathological monotonic clock value (e.g. u64::MAX-adjacent tick on an embedded/VM host with a broken clock, or after enormous fake-time offsets in tests using paused/auto-advance tokio time).

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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