astrid-runtime/astrid · error

WinFsp mountpoint is not a directory: {}

Error message

WinFsp mountpoint is not a directory: {}

What it means

wait_for_mountpoint_ready polls the mountpoint until std::fs::metadata reports a directory. A directory mount appears as a WinFsp junction; the code follows the reparse point so readiness means the mounted root actually serves I/O. If the path exists but metadata says it is not a directory (typically a regular file), the wait fails immediately with this error.

Source

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

    stdout.flush().context("flush WinFsp readiness")?;

    let result = runtime.block_on(daemon_loop(filesystem, control_listener));
    if let Err(error) = result {
        eprintln!("{PROVIDER_NAME}: daemon stopped after failure: {error:#}");
        return Err(error);
    }
    Ok(())
}

fn wait_for_mountpoint_ready(mountpoint: &Path) -> Result<()> {
    let started = Instant::now();
    loop {
        // A directory mount is represented by a WinFsp junction. Follow that
        // reparse point so readiness proves the mounted root is serving I/O,
        // rather than inspecting the junction object itself.
        match std::fs::metadata(mountpoint) {
            Ok(metadata) if metadata.is_dir() => return Ok(()),
            Ok(_) => bail!(
                "WinFsp mountpoint is not a directory: {}",
                mountpoint.display()
            ),
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => {},
            Err(error) => {
                return Err(error).with_context(|| {
                    format!(
                        "inspect WinFsp mountpoint readiness {}",
                        mountpoint.display()
                    )
                });
            },
        }
        if started.elapsed() >= MOUNTPOINT_READY_TIMEOUT {
            bail!(
                "WinFsp mountpoint did not become ready within {} seconds: {}",
                MOUNTPOINT_READY_TIMEOUT.as_secs(),
                mountpoint.display()

View on GitHub (pinned to affd8760f4)

Solutions

  1. Delete or rename the file occupying the mountpoint path and create a directory in its place (mkdir)
  2. Pick a different mountpoint path that does not exist or is a directory
  3. Before mounting, check fs::metadata and abort early if the path exists but is not a directory

Example fix

// before
std::fs::write("C:\\mounts\\astrid", "")?; // creates a file at the mountpoint
mount(...)?;
// after
if let Ok(md) = std::fs::metadata("C:\\mounts\\astrid") {
    assert!(md.is_dir(), "mountpoint must be a directory");
} else {
    std::fs::create_dir_all("C:\\mounts\\astrid")?;
}
mount(...)?;
Defensive patterns

Strategy: validation

Validate before calling

if let Ok(md) = std::fs::metadata(&mountpoint) {
    if !md.is_dir() {
        return Err(format!("{} exists and is not a directory; remove it or choose another path", mountpoint.display()));
    }
}

Type guard

fn is_directory(p: &Path) -> bool {
    std::fs::metadata(p).map(|m| m.is_dir()).unwrap_or(false)
}

Try / catch

match mount_err {
    Err(e) if e.to_string().contains("not a directory") => {
        let _ = std::fs::remove_file(&mountpoint);
        std::fs::create_dir_all(&mountpoint)?;
        retry_mount()?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: A regular file exists at the requested mountpoint path, so when the poll finds Ok(metadata) and metadata.is_dir() is false, it bails with the formatted path.

Common situations: Creating the mountpoint with `touch` or a tool that made a file instead of a directory; a leftover file from a previous run at the same path; a stale junction replaced by a file.

Related errors


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