astrid-runtime/astrid · error
lock FUSE provider lifecycle registry
Error message
lock FUSE provider lifecycle registry: {error} What it means
The FUSE provider serializes lifecycle operations with an exclusive flock on <registry-dir>/.lock. lock_registry opens/creates that file and takes Flock::lock exclusively; if flock fails, the underlying io::Error is wrapped as this message.
Solutions
- Check for another astrid FUSE process holding the lock (lsof/fuser on the .lock file) and wait or kill it
- Ensure the registry directory is on a local filesystem that supports flock
- Fix permissions so the current user can create/write <registry-dir>/.lock (mode 0o600)
- Remove a stale .lock file left by a dead process if no holder exists
Example fix
// before: bare failure
Flock::lock(file, FlockArg::LockExclusive)
.map_err(|(_, error)| anyhow::anyhow!("lock FUSE provider lifecycle registry: {error}"))
// after: retry with timeout before giving up
Flock::lock(file, FlockArg::LockExclusive)
.map_err(|(_, error)| anyhow::anyhow!("lock FUSE provider lifecycle registry: {error}"))
.with_context(|| format!("registry dir: {}", directory.display())) Defensive patterns
Strategy: try-catch
Validate before calling
// check no live holder before locking
let holder = std::process::Command::new("fuser")
.arg(directory.join(".lock"))
.output(); // non-empty output means a process holds it Try / catch
match lock_registry() {
Err(e) if e.to_string().contains("lock FUSE provider lifecycle registry") => {
// wait/backoff, or remove stale .lock if no holder
}
r => r,
} Prevention
- Serialize lifecycle commands (avoid parallel mount/unmount of same provider)
- Keep the registry directory on a local filesystem supporting flock
- Use a lock timeout with retry/backoff
- After crashes, verify no holder before deleting the stale .lock
When it happens
Trigger: Two mount/unmount operations racing and one process dying while holding the lock; the lock file living on a filesystem that does not support flock (some network FS); permission problems creating/opening .lock in the registry directory.
Common situations: Concurrent astrid mount/unmount commands from multiple shells or systemd units; a container with the registry dir on an NFS/overlay volume lacking flock support; read-only or wrong-permission XDG/state directory.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- failed to acquire daemon start fence
- shutdown stage daemon.singleton_lock: lock remains held at
- AlreadyExists
- AlreadyExists
- an incomplete capsule authority update exists at
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/da840a0603856a00.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage-provider-fuse/src/registry.rs:43
pub auto_created_mountpoint: bool,
/// Private detached-service control socket.
pub control_path: PathBuf,
}
/// Acquire the process-wide lifecycle lock used to serialize mount admission.
pub(crate) fn lock_registry() -> Result<Flock<File>> {
let directory = registry_directory()?;
astrid_core::platform_fs::ensure_private_directory(&directory)?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.mode(0o600)
.open(directory.join(".lock"))
.context("open FUSE provider lifecycle lock")?;
Flock::lock(file, FlockArg::LockExclusive)
.map_err(|(_, error)| anyhow::anyhow!("lock FUSE provider lifecycle registry: {error}"))
}
/// Load all records in mount-id order.
pub(crate) fn load_registry() -> Result<BTreeMap<String, MountRecord>> {
let directory = registry_directory()?;
let mut records = BTreeMap::new();
let entries = std::fs::read_dir(&directory);
let entries = match entries {
Ok(entries) => entries,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(records),
Err(error) => return Err(error).context("read FUSE provider registry"),
};
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.extension().is_none_or(|extension| extension != "json") {
continue;
}View on GitHub (pinned to affd8760f4)