astrid-runtime/astrid · error

WinFsp daemon exited immediately after readiness

Error message

WinFsp daemon exited immediately after readiness

What it means

Immediately after reading a valid READY line, spawn_daemon calls child.try_wait(); if the daemon has already exited, startup is aborted. This catches daemons that print READY and then crash before serving, which would otherwise leave a half-initialized mount.

Solutions

  1. Run the daemon manually with the same args and read its stderr for the post-READY crash cause.
  2. Install/repair the WinFsp driver and runtime on the host.
  3. Ensure the daemon only prints READY after the control endpoint and service loop are fully initialized.
  4. Check antivirus/EDR or resource limits (OOM) that may be killing the freshly spawned process.
Defensive patterns

Strategy: retry

Validate before calling

if child.try_wait()?.is_some() { eprintln!("daemon died right after READY"); }

Try / catch

match spawn_daemon(&lease, &launch).await {
    Err(e) if e.to_string().contains("exited immediately after readiness") => {
        // capture daemon stderr/stdout, repair environment (driver/runtime), retry once
        Err(e)
    },
    other => other,
}

Prevention

When it happens

Trigger: The daemon writes "READY {mount_id}\n" to stdout and then exits (panic after handshake, missing WinFsp driver, failed service loop init) before try_wait is polled.

Common situations: Missing or broken WinFsp driver installation on the Windows host; the service loop fails to bind its control endpoint after printing READY; daemon killed by a supervisor or OOM; DLL/runtime missing so the process dies right after startup.

Related errors


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

Appendix: source

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

            .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)