astrid-runtime/astrid · error

WinFsp daemon did not report readiness within 30 seconds

Error message

WinFsp daemon did not report readiness within 30 seconds

What it means

spawn_daemon wraps the readiness handshake in tokio::time::timeout(DAEMON_READY_TIMEOUT) of 30 seconds. If the daemon never prints a valid READY line within that window, it is killed and this error is raised, preventing indefinite hangs on a wedged daemon.

Source

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

            bail!("WinFsp daemon returned invalid readiness: {ready:?}");
        }
        if child.try_wait().context("inspect WinFsp daemon")?.is_some() {
            bail!("WinFsp daemon exited immediately after readiness");
        }
        Result::<()>::Ok(())
    };

    match tokio::time::timeout(DAEMON_READY_TIMEOUT, success).await {
        Ok(Ok(())) => Ok(()),
        Ok(Err(error)) => {
            let _ = child.kill().await;
            let _ = child.wait().await;
            Err(error.context("start WinFsp native filesystem"))
        },
        Err(_) => {
            let _ = child.kill().await;
            let _ = child.wait().await;
            bail!("WinFsp daemon did not report readiness within 30 seconds");
        },
    }
}

fn native_mountpoint(mountpoint: &Path) -> Result<PathBuf> {
    let text = mountpoint
        .to_str()
        .context("WinFsp mountpoint is not valid Unicode")?;
    let bytes = text.as_bytes();
    if bytes.len() == 3
        && bytes[0].is_ascii_alphabetic()
        && bytes[1] == b':'
        && matches!(bytes[2], b'\\' | b'/')
    {
        // FspFileSystemSetMountPoint accepts drive designators (`X:`), not
        // drive-root paths (`X:\\`). Keep the latter in the public lifecycle
        // record while passing the native spelling to WinFsp.
        return Ok(PathBuf::from(&text[..2]));

View on GitHub (pinned to affd8760f4)

Solutions

  1. Check the daemon's stderr/log output during the 30s window to find where startup blocks.
  2. Fix stdout flushing in the daemon (use println! + flush or LineWriter) so READY is emitted promptly.
  3. Pre-install/warm the WinFsp driver so first-start initialization fits the timeout.
  4. Verify the launched command and arguments are correct and the binary doesn't wait on stdin.

Example fix

// before (daemon)
write!(stdout, "READY {}", mount_id)?; // buffered, never flushed
// after
let mut stdout = io::stdout();
writeln!(stdout, "READY {}", mount_id)?;
stdout.flush()?;
Defensive patterns

Strategy: retry

Try / catch

match spawn_daemon(&lease, &launch).await {
    Err(e) if e.to_string().contains("did not report readiness within 30 seconds") => {
        // daemon was killed; inspect its stderr log, then retry after fixing startup stall
        Err(e)
    },
    other => other,
}

Prevention

When it happens

Trigger: The spawned daemon produces no READY line within 30s: it blocks on driver loading, waits on a missing dependency, deadlocks during init, or stdout is not flushed.

Common situations: Slow WinFsp driver installation first-run; daemon blocked on a prompt or network call at startup; stdout buffering so READY exists but is never flushed; the wrong executable launched (e.g. a shell script that waits for input).

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.

Related errors


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