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
- Check the wrapped errno: EPERM means run as root / with CAP_SYS_ADMIN
- If ENOENT, the mountpoint no longer exists — treat the record as already cleaned and remove it from the registry
- Verify the path in the error message is a real, currently mounted FUSE mountpoint (mount/findmnt)
- 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
- Check /proc/mounts or findmnt before unmounting
- Run unmounts with sufficient privileges (root/CAP_SYS_ADMIN)
- Prune registry records whose mountpoints no longer exist
- Always pass absolute paths
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
- FUSE mount completed but is absent from the Linux mount tabl
- macOS unmount failed with {status}
- FUSE service unmount failed [{code}]: {message}
- FUSE service returned an incompatible unmount response
- mountpoint must be owned by the current OS user: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ee3c6dedbd241c22.
Report an issue: GitHub.