{"record":{"id":"801af999b94def3a","repo":"clockworklabs/SpacetimeDB","slug":"failed-to-open-directory-for-fsync","errorCode":null,"errorMessage":"failed to open directory {} for fsync: {}","messagePattern":"failed to open directory (.+?) for fsync: (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/snapshot/src/lib.rs","lineNumber":1630,"sourceCode":"    Dir(&'a Path),\n}\n\nimpl FileOrDirPath<'_> {\n    /// `fsync` the file or directory at path `self`.\n    ///\n    /// On *nix systems, both a file and its enclosing directory should be\n    /// `fsync`ed to make the file durable.\n    ///\n    /// On Windows, only the file needs to be synced, and it's even an error to\n    /// sync a directory. Passing in [Self::Dir] is thus a no-op on Windows.\n    fn sync_all(&self) -> io::Result<()> {\n        match self {\n            #[cfg(target_os = \"windows\")]\n            Self::Dir(path) => Ok(()),\n            #[cfg(not(target_os = \"windows\"))]\n            Self::Dir(path) => File::open(path)\n                .map_err(|e| {\n                    io::Error::new(\n                        e.kind(),\n                        format!(\"failed to open directory {} for fsync: {}\", path.display(), e),\n                    )\n                })?\n                .sync_all()\n                .map_err(|e| io::Error::new(e.kind(), format!(\"failed to fsync directory {}: {}\", path.display(), e))),\n            Self::File(path) => {\n                File::options()\n                    .read(true)\n                    // Windows needs the file to be writable for `sync_all` to work.\n                    // Set all the open options explicitly, just for visibility.\n                    .write(true)\n                    .truncate(false)\n                    .create(false)\n                    .append(false)\n                    .open(path)\n                    .map_err(|e| {\n                        io::Error::new(","sourceCodeStart":1612,"sourceCodeEnd":1648,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/9e0d92412ff2248f401a8ad12d535f2b5ac30912/crates/snapshot/src/lib.rs#L1612-L1648","documentation":"crates/snapshot's sync helper fsyncs both files and (on Unix) their enclosing directories for durability. For Self::Dir(path) it must File::open the directory first, since directories are opened for fsync rather than created. If that open fails, the original io::Error kind is preserved and wrapped with 'failed to open directory {path} for fsync: {source}'. Typical underlying causes: the directory was deleted or renamed between snapshot and sync, permissions changed, or the path is not a directory.","triggerScenarios":"Snapshot/restore code syncing a directory that a concurrent process removed or moved; running under a user without read (open) permission on the directory; a path that points at a file rather than a directory; container/overlay filesystems that reject opening directories.","commonSituations":"Concurrent cleanup (tmp cleaners, GC of snapshot dirs) racing with durability sync; restrictive permissions in hardened deployments; NFS/FUSE mounts with odd open semantics for directories.","solutions":["Check that the directory still exists and is a directory right before syncing; re-create it if the workflow allows","Fix permissions so the process can open (read) the directory it needs to sync","Serialize directory cleanup with snapshot finalization so they cannot race","Match on io::ErrorKind in the caller (NotFound/PermissionDenied) to decide between recreate, retry, and abort"],"exampleFix":"// before\nSyncTarget::Dir(&dir).sync_all()?; // NotFound: dir removed by cleaner mid-snapshot\n\n// after\nif !dir.is_dir() {\n    std::fs::create_dir_all(&dir)?; // or: skip sync if dir intentionally gone\n}\nSyncTarget::Dir(&dir).sync_all()?;","handlingStrategy":"try-catch","validationCode":"if !dir.as_path().is_dir() {\n    std::fs::create_dir_all(&dir)?; // or skip if legitimately gone\n}","typeGuard":"fn is_syncable_dir(p: &Path) -> bool { p.is_dir() }","tryCatchPattern":"if let Err(e) = SyncTarget::Dir(&dir).sync_all() {\n    if e.kind() == std::io::ErrorKind::NotFound { /* recreated/removed concurrently: decide policy */ }\n    else if e.kind() == std::io::ErrorKind::PermissionDenied { /* fix perms */ }\n    else { return Err(e); }\n}","preventionTips":["Serialize snapshot finalization with directory GC/cleanup","Ensure the process can open (read) directories it must sync","Keep snapshot trees on local POSIX filesystems"],"tags":["filesystem","fsync","durability","rust","snapshot","spacetimedb"],"backgroundTag":"fsync-failed","analyzedSha":"9e0d92412ff2248f401a8ad12d535f2b5ac30912","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}