astrid-runtime/astrid · error
cannot unmount a relative mountpoint
Error message
cannot unmount a relative mountpoint
What it means
lazy_unmount only performs a lazy detach (umount2 with MNT_DETACH) on absolute paths; a relative path could resolve to a different location depending on cwd, so it is rejected up front. This keeps the unmount target unambiguous.
Solutions
- Canonicalize the path before unmounting (std::fs::canonicalize or store the canonical path from prepare_mountpoint)
- Ensure the path is absolute (starts with /) before calling lazy_unmount
- Persist the canonical mountpoint returned by the mount flow, not the user input
Example fix
// before
lazy_unmount(Path::new("./mount"))?
// after
let canonical = std::fs::canonicalize("./mount")?;
lazy_unmount(&canonical)? Defensive patterns
Strategy: validation
Validate before calling
fn ensure_absolute(path: &std::path::Path) -> Option<&std::path::Path> {
path.is_absolute().then_some(path)
} Type guard
fn is_absolute_path(path: &std::path::Path) -> bool {
path.is_absolute()
} Try / catch
match lazy_unmount(path) {
Err(e) if e.to_string().contains("relative mountpoint") => {
let canonical = std::fs::canonicalize(path)?;
lazy_unmount(&canonical)?
}
other => other?,
} Prevention
- Store the canonical path returned by prepare_mountpoint for later unmounts
- Call std::fs::canonicalize before unmounting user-supplied paths
- Never persist relative paths in session/registry state
- Assert path.is_absolute() at configuration load time
When it happens
Trigger: Calling lazy_unmount with a relative Path (e.g. "my-mount" or "./mount") instead of an absolute path; passing a mountpoint string that was never canonicalized.
Common situations: Storing the user-supplied mountpoint string in config instead of the canonical path returned by prepare_mountpoint; reconstructing the path relative to a working directory.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- detached FUSE service failed
- detached FUSE stderr drain failed
- detached FUSE stderr sink handoff timed out
- FUSE mount completed but is absent from the Linux mount…
- FUSE service mountpoint is already mounted
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7c61b05ec4c865fc.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage-provider-fuse/src/mountpoint.rs:76
.canonicalize()
.with_context(|| format!("canonicalize mountpoint {}", requested.display()))?;
if mountinfo_contains(&canonical)? {
bail!("mountpoint is already mounted: {}", canonical.display());
}
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(mountsView on GitHub (pinned to affd8760f4)