astrid-runtime/astrid · error

lazy unmount {}: {error}

Error message

lazy unmount {}: {error}

What it means

lazy_unmount performs MNT_DETACH on the given path via nix::mount::umount2. If the kernel returns an errno other than success or EINVAL (already-unmounted, treated as ok), the error is wrapped as 'lazy unmount <path>: <errno>'.

Source

Thrown at crates/astrid-storage-provider-fuse/src/mountpoint.rs:80

    }
    Ok((canonical, !existed))
}

/// Return current owner identity for synthetic inode metadata.
pub(crate) fn owner_ids() -> (u32, u32) {
    (getuid().into(), getgid().into())
}

/// Remove a dead FUSE mount if one remains at an exact canonical path.
pub(crate) fn lazy_unmount(mountpoint: &Path) -> Result<()> {
    use nix::mount::MntFlags;

    if !mountpoint.is_absolute() {
        bail!("cannot unmount a relative mountpoint");
    }
    match nix::mount::umount2(mountpoint, MntFlags::MNT_DETACH) {
        Ok(()) | Err(nix::errno::Errno::EINVAL) => Ok(()),
        Err(error) => Err(anyhow::anyhow!(
            "lazy unmount {}: {error}",
            mountpoint.display()
        )),
    }
}

/// Whether `/proc/self/mountinfo` has a mount at this exact path.
pub(crate) fn mountinfo_contains(mountpoint: &Path) -> Result<bool> {
    let expected = mountpoint
        .to_str()
        .context("mountpoint must be canonical Unicode text")?
        .as_bytes();
    let mounts = std::fs::read("/proc/self/mountinfo").context("read Linux mount table")?;
    Ok(mounts
        .split(|byte| *byte == b'\n')
        .filter(|line| !line.is_empty())
        .any(|line| {
            line.split(|byte| *byte == b' ')

View on GitHub (pinned to affd8760f4)

Solutions

  1. Check the wrapped errno: EPERM means run as root / with CAP_SYS_ADMIN
  2. If ENOENT, the mountpoint no longer exists — treat the record as already cleaned and remove it from the registry
  3. Verify the path in the error message is a real, currently mounted FUSE mountpoint (mount/findmnt)
  4. Ensure the mountpoint path is absolute; relative paths are rejected earlier

Example fix

// before: any errno surfaces as failure
match nix::mount::umount2(mountpoint, MntFlags::MNT_DETACH) {
    Ok(()) | Err(nix::errno::Errno::EINVAL) => Ok(()),
// after: also tolerate already-gone mountpoints
    Ok(()) | Err(nix::errno::Errno::EINVAL) | Err(nix::errno::Errno::ENOENT) => Ok(()),
Defensive patterns

Strategy: validation

Validate before calling

// before calling unmount
if !mountpoint.is_absolute() { bail!("relative path"); }
if !Path::new("/proc/mounts").to_path_buf().exists() ||
   !std::fs::read_to_string("/proc/mounts")?.contains(mountpoint.as_os_str()) {
    // not mounted — skip umount2
}

Try / catch

match result {
    Ok(()) => (),
    Err(e) if e.to_string().contains("EPERM") => bail!("need root/CAP_SYS_ADMIN"),
    Err(e) if e.to_string().contains("ENOENT") => (), // already gone
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling lazy_unmount on a path that is not a mountpoint (ENOENT), is owned by another user without privileges (EPERM), or is in a state where detach is refused (EBUSY in restrictive configs).

Common situations: Mountpoint was already unmounted by another process or crashed daemon; running without root/CAP_SYS_ADMIN in a container; the registry record points to a deleted path; typo'd or stale mountpoint path.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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