{"record":{"id":"8b653a45e2cb8253","repo":"tonhowtf/omniget","slug":"rename-part-to-final-failed","errorCode":null,"errorMessage":"rename .part to final failed: {}","messagePattern":"rename \\.part to final failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/http_fetcher.rs","lineNumber":261,"sourceCode":"\n        ensure_part_file(&part_path, total).await?;\n\n        let result = self\n            .download_chunked(\n                &part_path,\n                total,\n                segments,\n                &resume_path,\n                &url_hash,\n                &progress_tx,\n            )\n            .await;\n\n        match result {\n            Ok(()) => {\n                tokio::fs::rename(&part_path, &self.output_path)\n                    .await\n                    .map_err(|e| anyhow!(\"rename .part to final failed: {}\", e))?;\n                if self.config.use_sidecar_resume {\n                    let _ = tokio::fs::remove_file(&resume_path).await;\n                }\n                let _ = progress_tx.send(ProgressUpdate::percent(100.0)).await;\n                let bytes = tokio::fs::metadata(&self.output_path)\n                    .await\n                    .map(|m| m.len())\n                    .unwrap_or(total);\n                Ok(HttpFetcherResult {\n                    bytes_written: bytes,\n                })\n            }\n            Err(e) => Err(e),\n        }\n    }\n\n    async fn probe(&self) -> anyhow::Result<ProbeResult> {\n        let remote = probe_remote(","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/http_fetcher.rs#L243-L279","documentation":"This error is thrown in HttpFetcher::download after all chunks have been written to the `<file>.part` file and the fetcher attempts to atomically rename the .part file to the final output path. tokio::fs::rename failed, so the completed download data exists in the .part file but was not moved into place. The library returns the underlying io::Error message verbatim.","triggerScenarios":"tokio::fs::rename(&part_path, &self.output_path) returns Err after a successful download. On Unix this happens when the output directory no longer exists, permission is denied on the target directory, output_path is an existing non-empty directory, or the filesystem is full/read-only. On Windows it also fails if the destination file is locked/open by another process.","commonSituations":"Output directory deleted or moved while a long download ran; antivirus or another process holding the target file open (Windows); writing to a read-only or full disk; output_path points into a directory the user lacks write permission for; cross-device rename when part_path and output_path resolve onto different mount points.","solutions":["Check that the output directory exists and is writable before starting the download (create it with create_dir_all).","On Windows, close any process (editor/antivirus/backup) that has the destination file open, or retry after the lock is released.","Verify free disk space and that the target filesystem is not mounted read-only.","Ensure output_path is a file path, not an existing directory, and that .part and final paths are on the same filesystem.","Recover manually: the completed data is still in the .part file next to output_path — rename it yourself or resume/retry the download."],"exampleFix":"// before\nlet fetcher = HttpFetcher::new(url, \"/mnt/readonly/out.zip\");\n\n// after\nlet out = Path::new(\"/mnt/data/out.zip\");\ntokio::fs::create_dir_all(out.parent().unwrap()).await?;\nlet fetcher = HttpFetcher::new(url, out.to_path_buf());","handlingStrategy":"try-catch","validationCode":"let out = Path::new(&output_path);\nlet dir = out.parent().ok_or(\"no parent dir\")?;\ntokio::fs::create_dir_all(dir).await?;\nlet md = tokio::fs::metadata(dir).await?;\nif md.permissions().readonly() {\n    return Err(anyhow!(\"output dir not writable\"));\n}\nif tokio::fs::metadata(out).await.map(|m| m.is_dir()).unwrap_or(false) {\n    return Err(anyhow!(\"output path is a directory\"));\n}","typeGuard":"fn valid_output_target(p: &Path) -> bool {\n    p.parent().is_some() && !p.is_dir()\n}","tryCatchPattern":"match fetcher.download(&mut tx).await {\n    Err(e) if e.to_string().contains(\"rename .part to final failed\") => {\n        // recover: completed data is still in the .part file\n        let part = output_path.with_extension(\"part\");\n        tokio::fs::rename(&part, &output_path).await\n            .or_else(|_| retry_download().await)?;\n    }\n    other => other?,\n}","preventionTips":["Always create_dir_all the output directory before starting a download.","On Windows, avoid pointing output_path at files that editors/AV/indexers keep open.","Check free disk space and mount status for long downloads.","Keep .part and final output on the same filesystem/volume."],"tags":["filesystem","rename","io","download"],"backgroundTag":"file-write-failed","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}