astrid-runtime/astrid · error

WinFsp daemon returned invalid readiness

Error message

WinFsp daemon returned invalid readiness: {ready:?}

What it means

spawn_daemon waits for the daemon's stdout readiness line, which must be exactly "READY {mount_id}\n". Any other line (different text, different mount id, extra whitespace, or a wrapped error message) is rejected, ensuring the daemon is alive, is the right mount, and follows the handshake contract.

Solutions

  1. Run the daemon with the correct mount_id argument matching lease.mount_id.
  2. Strip banner/diagnostic output (send logging to stderr) so READY is the first stdout line.
  3. Check for CRLF: the daemon must emit "READY {id}\n", not "\r\n"; fix eprintln/print usage.
  4. Verify the daemon executable version matches the library's handshake protocol.

Example fix

// before (daemon)
println!("READY {}\r\n", mount_id); // CRLF mismatch
// after
println!("READY {}", mount_id);
Defensive patterns

Strategy: try-catch

Validate before calling

let expected = format!("READY {}\n", lease.mount_id);
// after reading the line: assert_eq!(ready, expected);

Type guard

fn is_valid_ready_line(line: &str, mount_id: &str) -> bool {
    line == format!("READY {mount_id}\n")
}

Try / catch

match spawn_daemon(&lease, &launch).await {
    Err(e) if e.to_string().contains("invalid readiness") => {
        // inspect daemon stderr, verify binary version and mount_id argument
        Err(e)
    },
    other => other,
}

Prevention

When it happens

Trigger: spawn_daemon reads the first stdout line and it does not equal format!("READY {}\n", lease.mount_id): wrong mount_id passed to the daemon, daemon prints banner/diagnostics before READY, \r\n line endings on Windows, or a panic message on stdout.

Common situations: The daemon binary on PATH is a different/older version without the READY handshake; launch arguments transposed so the daemon receives another mount id; stdout redirected through a wrapper that adds prefixes; Windows CRLF line endings.

Related errors


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

Appendix: source

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

        stdin
            .write_all(b"\n")
            .await
            .context("terminate daemon lease")?;
        drop(stdin);

        let stdout = child
            .stdout
            .take()
            .context("WinFsp daemon stdout is unavailable")?;
        let mut stdout = tokio::io::BufReader::new(stdout);
        let mut ready = String::new();
        stdout
            .read_line(&mut ready)
            .await
            .context("read WinFsp daemon readiness")?;
        let expected = format!("READY {}\n", lease.mount_id);
        if ready != expected {
            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");

View on GitHub (pinned to affd8760f4)