tonhowtf/omniget · error · anyhow::Error
rename .part to final failed
Error message
rename .part to final failed: {} What it means
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.
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.
Example fix
// before
let fetcher = HttpFetcher::new(url, "/mnt/readonly/out.zip");
// after
let out = Path::new("/mnt/data/out.zip");
tokio::fs::create_dir_all(out.parent().unwrap()).await?;
let fetcher = HttpFetcher::new(url, out.to_path_buf()); Defensive patterns
Strategy: try-catch
Validate before calling
let out = Path::new(&output_path);
let dir = out.parent().ok_or("no parent dir")?;
tokio::fs::create_dir_all(dir).await?;
let md = tokio::fs::metadata(dir).await?;
if md.permissions().readonly() {
return Err(anyhow!("output dir not writable"));
}
if tokio::fs::metadata(out).await.map(|m| m.is_dir()).unwrap_or(false) {
return Err(anyhow!("output path is a directory"));
} Type guard
fn valid_output_target(p: &Path) -> bool {
p.parent().is_some() && !p.is_dir()
} Try / catch
match fetcher.download(&mut tx).await {
Err(e) if e.to_string().contains("rename .part to final failed") => {
// recover: completed data is still in the .part file
let part = output_path.with_extension("part");
tokio::fs::rename(&part, &output_path).await
.or_else(|_| retry_download().await)?;
}
other => other?,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Write error (disk full?)
- Failed to move into place
- Failed to replace
- gallery-dl binary not found after download
- nao foi possivel mover a instalacao para o lugar
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/8b653a45e2cb8253.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/http_fetcher.rs:261
ensure_part_file(&part_path, total).await?;
let result = self
.download_chunked(
&part_path,
total,
segments,
&resume_path,
&url_hash,
&progress_tx,
)
.await;
match result {
Ok(()) => {
tokio::fs::rename(&part_path, &self.output_path)
.await
.map_err(|e| anyhow!("rename .part to final failed: {}", e))?;
if self.config.use_sidecar_resume {
let _ = tokio::fs::remove_file(&resume_path).await;
}
let _ = progress_tx.send(ProgressUpdate::percent(100.0)).await;
let bytes = tokio::fs::metadata(&self.output_path)
.await
.map(|m| m.len())
.unwrap_or(total);
Ok(HttpFetcherResult {
bytes_written: bytes,
})
}
Err(e) => Err(e),
}
}
async fn probe(&self) -> anyhow::Result<ProbeResult> {
let remote = probe_remote(View on GitHub (pinned to 8600b91f42)