{"record":{"id":"da840a0603856a00","repo":"astrid-runtime/astrid","slug":"lock-fuse-provider-lifecycle-registry-error","errorCode":null,"errorMessage":"lock FUSE provider lifecycle registry: {error}","messagePattern":"lock FUSE provider lifecycle registry: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fuse/src/registry.rs","lineNumber":43,"sourceCode":"    pub auto_created_mountpoint: bool,\n    /// Private detached-service control socket.\n    pub control_path: PathBuf,\n}\n\n/// Acquire the process-wide lifecycle lock used to serialize mount admission.\npub(crate) fn lock_registry() -> Result<Flock<File>> {\n    let directory = registry_directory()?;\n    astrid_core::platform_fs::ensure_private_directory(&directory)?;\n    let file = OpenOptions::new()\n        .read(true)\n        .write(true)\n        .create(true)\n        .truncate(false)\n        .mode(0o600)\n        .open(directory.join(\".lock\"))\n        .context(\"open FUSE provider lifecycle lock\")?;\n    Flock::lock(file, FlockArg::LockExclusive)\n        .map_err(|(_, error)| anyhow::anyhow!(\"lock FUSE provider lifecycle registry: {error}\"))\n}\n\n/// Load all records in mount-id order.\npub(crate) fn load_registry() -> Result<BTreeMap<String, MountRecord>> {\n    let directory = registry_directory()?;\n    let mut records = BTreeMap::new();\n    let entries = std::fs::read_dir(&directory);\n    let entries = match entries {\n        Ok(entries) => entries,\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(records),\n        Err(error) => return Err(error).context(\"read FUSE provider registry\"),\n    };\n    for entry in entries {\n        let entry = entry?;\n        let path = entry.path();\n        if path.extension().is_none_or(|extension| extension != \"json\") {\n            continue;\n        }","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fuse/src/registry.rs#L25-L61","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: bare failure\nFlock::lock(file, FlockArg::LockExclusive)\n    .map_err(|(_, error)| anyhow::anyhow!(\"lock FUSE provider lifecycle registry: {error}\"))\n// after: retry with timeout before giving up\nFlock::lock(file, FlockArg::LockExclusive)\n    .map_err(|(_, error)| anyhow::anyhow!(\"lock FUSE provider lifecycle registry: {error}\"))\n    .with_context(|| format!(\"registry dir: {}\", directory.display()))","handlingStrategy":"try-catch","validationCode":"// check no live holder before locking\nlet holder = std::process::Command::new(\"fuser\")\n    .arg(directory.join(\".lock\"))\n    .output(); // non-empty output means a process holds it","typeGuard":null,"tryCatchPattern":"match lock_registry() {\n    Err(e) if e.to_string().contains(\"lock FUSE provider lifecycle registry\") => {\n        // wait/backoff, or remove stale .lock if no holder\n    }\n    r => r,\n}","preventionTips":["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"],"tags":["flock","concurrency","fuse","file-lock"],"backgroundTag":"file-open-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}