{"record":{"id":"b910e9db9eb106bc","repo":"libnyanpasu/clash-nyanpasu","slug":"cleanup-tombstone-already-exists","errorCode":null,"errorMessage":"cleanup tombstone already exists: {}","messagePattern":"cleanup tombstone already exists: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":1473,"sourceCode":"                    removed += 1;\n                }\n            }\n        }\n        Ok(removed)\n    }\n\n    fn remove_cleanup_target(\n        &self,\n        root: &Path,\n        operation_id: &str,\n        target: &Path,\n    ) -> anyhow::Result<()> {\n        #[cfg(windows)]\n        {\n            let tombstone = Self::cleanup_tombstone_path(root, operation_id);\n            match std::fs::symlink_metadata(&tombstone) {\n                Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}\n                Ok(_) => bail!(\"cleanup tombstone already exists: {}\", tombstone.display()),\n                Err(error) => {\n                    return Err(error).with_context(|| {\n                        format!(\"inspect cleanup tombstone {}\", tombstone.display())\n                    });\n                }\n            }\n            self.ensure_managed_parent(target)?;\n            Self::ensure_replaceable_target(target)?;\n            replace_atomic(target, &tombstone).with_context(|| {\n                format!(\n                    \"move cleanup target {} to tombstone {}\",\n                    target.display(),\n                    tombstone.display()\n                )\n            })\n        }\n        #[cfg(not(windows))]\n        {","sourceCodeStart":1455,"sourceCodeEnd":1491,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L1455-L1491","documentation":"On Windows, destructive profile cleanup first records a tombstone file (cleanup_tombstone_path) marking the operation as in-progress so a crash can't skip the deletion. Before creating it, the code checks the tombstone doesn't already exist; if it does, the operation bails. A pre-existing tombstone means another cleanup with the same operation_id is (or was) already running, and proceeding could double-delete or race.","triggerScenarios":"Starting (or retrying) a profile cleanup whose Windows tombstone file for the same operation_id already exists — a previous attempt with the same id crashed before removing the tombstone, two concurrent cleanups were launched with the same id, or a leftover tombstone from a test run.","commonSituations":"Retrying a failed cleanup without regenerating the operation id; parallel UI actions triggering cleanup twice; interrupted runs on Windows where the tombstone was written but cleanup never completed; stale artifacts left by manual testing.","solutions":["Remove the stale tombstone at the reported path only after confirming no cleanup process is currently running, then retry.","Prefer starting the cleanup with a fresh operation_id so the old tombstone is simply ignored by the new attempt.","Serialize cleanup calls in your application (one at a time) to avoid two attempts sharing an id.","If a previous cleanup is actually mid-flight, wait for it to finish instead of deleting its tombstone."],"exampleFix":"// before: retry reuses the old id and hits the tombstone\nrun_cleanup(root, &old_id)?;\n// after: new attempt, new id (old tombstone no longer conflicts)\nlet id = generate_operation_id();\nrun_cleanup(root, &id)?;\n// or, once certain nothing is running:\nlet _ = std::fs::remove_file(cleanup_tombstone_path(root, &old_id));","handlingStrategy":"validation","validationCode":"fn tombstone_free(root: &Path, id: &str) -> bool {\n    match std::fs::symlink_metadata(cleanup_tombstone_path(root, id)) {\n        Err(e) => e.kind() == std::io::ErrorKind::NotFound,\n        Ok(_) => false,\n    }\n}","typeGuard":null,"tryCatchPattern":"match run_cleanup(root, id) {\n    Err(e) if e.to_string().contains(\"cleanup tombstone already exists\") => {\n        // confirm no cleanup is running, then remove the stale tombstone\n        // or restart the attempt with a fresh operation_id\n    }\n    other => other,\n}","preventionTips":["Generate a new operation_id for each cleanup attempt instead of retrying with the old one","Serialize cleanup invocations; never run two cleanups concurrently","Clean up stale tombstones after confirmed-crashed runs before restarting","Check for a live cleanup process before deleting any tombstone"],"tags":["windows","cleanup","concurrency","tombstone"],"backgroundTag":"file-already-exists","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}