{"record":{"id":"b4c0579942365269","repo":"gitbutlerapp/gitbutler","slug":"bug-we-do-not-create-or-work-with-symlinks","errorCode":null,"errorMessage":"BUG: we do not create or work with symlinks","messagePattern":"BUG: we do not create or work with symlinks","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-utils/src/lib.rs","lineNumber":86,"sourceCode":"        /// Delete the file or directory at `rela_path`.\n        ///\n        /// ### Panics\n        ///\n        /// If a symlink is encountered.\n        pub fn delete(&self, rela_path: impl AsRef<Path>) -> std::io::Result<()> {\n            let file_path = self.local_data_dir.join(rela_path);\n            let md = match file_path.symlink_metadata() {\n                Ok(md) => md,\n                Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),\n                Err(err) => return Err(err),\n            };\n\n            if md.is_dir() {\n                fs::remove_dir_all(file_path)?;\n            } else if md.is_file() {\n                fs::remove_file(file_path)?;\n            } else {\n                unreachable!(\"BUG: we do not create or work with symlinks\")\n            }\n            Ok(())\n        }\n    }\n}\n#[cfg(feature = \"legacy\")]\npub use legacy::Storage;\n\n// Returns an ordered list of relative paths for files inside a directory recursively.\npub fn list_files<P: AsRef<Path>>(\n    dir_path: P,\n    ignore_prefixes: &[P],\n    recursive: bool,\n    remove_prefix: Option<P>,\n) -> Result<Vec<PathBuf>> {\n    let mut files = vec![];\n    let dir_path = dir_path.as_ref();\n    if !dir_path.exists() {","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-utils/src/lib.rs#L68-L104","documentation":"Legacy `Storage` in but-utils removes files under the app-data dir using `symlink_metadata`; an entry that is neither a directory nor a regular file is assumed impossible because the app never creates symlinks or special files, so that branch panics. Reaching it means something (usually a symlink) was placed inside the GitButler app-data directory from outside (crates/but-utils/src/lib.rs:86).","triggerScenarios":"`Storage::clear()`-style removal while the app-data dir contains a symlink, FIFO, or socket - e.g. a user symlinked a cache subfolder to another disk, or a backup restore converted files into symlinks.","commonSituations":"Users relocating ~/.local/share/GitButler (or Library/Application Support/GitButler) subfolders via symlinks; partial restores from Time Machine/rsync with `-l` semantics; leftover mkfifo debug artifacts.","solutions":["Inspect the app-data dir for non-regular entries: `find <app-data> -type l` and remove or inline the offending symlinks, then retry the clear","If the symlink was intentional (disk relocation), remove it and use a bind mount / proper relocation instead, then retry","As a maintainer: handle `md.file_type().is_symlink()` with `fs::remove_file` (which unlinks the link) instead of panicking"],"exampleFix":"// before (but-utils/src/lib.rs)\nif md.is_dir() {\n    fs::remove_dir_all(file_path)?;\n} else if md.is_file() {\n    fs::remove_file(file_path)?;\n} else {\n    unreachable!(\"BUG: we do not create or work with symlinks\")\n}\n\n// after - unlink symlinks instead of panicking\nif md.is_dir() {\n    fs::remove_dir_all(file_path)?;\n} else {\n    // regular files, symlinks, and other non-dirs all unlink fine\n    fs::remove_file(file_path)?;\n}","handlingStrategy":"validation","validationCode":"// Scan the app-data dir for non-regular entries before clearing\nfn clearable(dir: &Path) -> std::io::Result<bool> {\n    for entry in std::fs::read_dir(dir)? {\n        let md = entry?.metadata()?; // follows symlinks; use symlink_metadata to detect them\n        let lmd = entry?.symlink_metadata()?;\n        if !(lmd.is_dir() || lmd.is_file()) { return Ok(false); }\n    }\n    Ok(true)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not symlink inside the application data directory; relocate the whole directory instead","After restoring from backup, run `find <app-data> -type l` and resolve any hits before launching the app","Maintainers: treat is_symlink as a removable entry (fs::remove_file unlinks it) instead of an unreachable"],"tags":["rust","filesystem","symlink","panic","app-data","but-utils"],"backgroundTag":"symlink-not-supported","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}