astrid-runtime/astrid · error
WinFsp stop timed out
Error message
WinFsp stop timed out
What it means
Raised in `stop_daemon` (crates/astrid-storage-provider-winfsp/src/win.rs:668) when the asynchronous stop sequence — connecting to the daemon's control endpoint, sending `STOP`, and reading the single-byte acknowledgement — does not complete within `DAEMON_STOP_TIMEOUT`. The timeout wrapper converts the elapsed future into this error. It indicates the WinFsp daemon is hung, unreachable in a non-NotFound way, or its control channel is unresponsive.
Source
Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:668
};
stream
.write_all(b"STOP")
.await
.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")
}
View on GitHub (pinned to affd8760f4)
Solutions
- Check whether the WinFsp service process is still running and inspect its logs for a stuck filesystem callback blocking the service loop.
- Verify nothing else is holding the control endpoint connection open (a leaked client keeps the stop handshake from completing).
- If the daemon is wedged, forcibly terminate the service process, then remove the leftover endpoint file before remounting.
- Retry `stop_daemon` once the system is under lighter load; if timeouts recur, consider raising `DAEMON_STOP_TIMEOUT`.
- Confirm the mountpoint was actually unmounted after force-kill to avoid the stale-mount state.
Example fix
// before
tokio::time::timeout(DAEMON_STOP_TIMEOUT, stop)
.await
.map_err(|_| anyhow::anyhow!("WinFsp stop timed out"))??;
// after (caller-side guard)
match tokio::time::timeout(DAEMON_STOP_TIMEOUT, stop_daemon(&control_path)).await {
Ok(Ok(())) => {}
Ok(Err(e)) => warn!(error = %e, "graceful stop failed; forcing teardown"),
Err(_) => { warn!("WinFsp stop timed out; killing daemon"); kill_daemon(&pid_path); }
} Defensive patterns
Strategy: retry
Validate before calling
// Confirm the daemon is responsive before attempting a stop
if !endpoint_is_present(control_path) {
return Ok(()); // already gone, no stop needed
} Type guard
fn daemon_stoppable(control_path: &Path) -> bool {
endpoint_is_present(control_path) && daemon_pid_is_alive(pid_path())
} Try / catch
match tokio::time::timeout(DAEMON_STOP_TIMEOUT, stop_daemon(&control_path)).await {
Ok(Ok(())) => info!("daemon stopped cleanly"),
Ok(Err(e)) => { warn!(error = %e, "stop failed; forcing teardown"); force_teardown(); }
Err(_elapsed) => { warn!("WinFsp stop timed out; killing daemon"); force_teardown(); }
} Prevention
- Keep filesystem callbacks non-blocking so the service loop can always answer the control socket.
- Detect and reap wedged daemon processes with a liveness watchdog.
- After a timeout, always force-kill and clean the endpoint file before remounting.
- Size DAEMON_STOP_TIMEOUT generously for heavily loaded hosts.
When it happens
Trigger: Specifically: `tokio::time::timeout(DAEMON_STOP_TIMEOUT, stop)` elapses. Cases: `local_transport::connect(control_path)` accepts but never completes, the `STOP` write never flushes, or the daemon never sends the `b'S'` acknowledgement byte before the deadline.
Common situations: The WinFsp daemon thread is blocked inside a filesystem callback and cannot service the control socket; the endpoint exists but the daemon process is wedged or being torn down by the OS; resource exhaustion (the daemon's runtime cannot make progress); a stale endpoint held by a dead process whose socket still accepts connections.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- WinFsp control endpoint remained live after stop
- WinFsp daemon lease exceeds limit
- WinFsp mountpoint did not become ready within {} seconds: {}
- WinFsp service launch exceeds limit
- WinFsp service parent process is not alive
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/0dd2d232afa48551.
Report an issue: GitHub.