tonhowtf/omniget · warning
Range not satisfiable, restarting
Error message
Range not satisfiable, restarting
What it means
When resuming, download_single_stream sends a Range request for the existing .part bytes. If the server answers 416 RANGE_NOT_SATISFIABLE instead of 206 PARTIAL_CONTENT, the stored offset is no longer valid (file shrank, changed, or the server can't satisfy the range), so the .part file is deleted and this error is returned to signal the caller that the transfer will restart from scratch. The retry loop treats it as a retryable attempt.
Solutions
- Do nothing special beyond retrying — the library deletes the stale .part and the next attempt restarts from byte 0; this is usually self-healing.
- Manually delete the .part file (output + '.part') before re-calling download if you want a guaranteed clean restart in one attempt.
- Verify the remote file hasn't changed (size/ETag/URL) between the original attempt and the resume; if it has, download to a new output path.
- Ensure the same URL/mirror is used for resume — switching mirrors with different content makes the saved offset invalid.
- If a proxy strips Range support, bypass it or download without resume.
Example fix
// before
// stale .part from an older, larger version of the file
let file = downloader.download(&url, &out).await?; // -> 416, 'Range not satisfiable, restarting'
// after
let part = out.with_extension("part"); // match part_path_for() naming
let _ = std::fs::remove_file(&part); // drop incompatible resume state up front
let file = downloader.download(&url, &out).await?; // clean full download Defensive patterns
Strategy: retry
Validate before calling
// Rust: drop resume state whose size can't fit the current remote file
fn reset_stale_part(output: &std::path::Path, remote_len: u64) {
let part = output.with_extension("part");
if let Ok(m) = std::fs::metadata(&part) {
if m.len() > remote_len {
let _ = std::fs::remove_file(&part);
}
}
} Try / catch
match downloader.download(&url, &out).await {
Err(e) if e.to_string().contains("Range not satisfiable") => {
// library already deleted the .part; one clean retry starts from scratch
downloader.download(&url, &out).await
}
other => other,
} Prevention
- Resume only against the same URL/mirror that produced the .part file
- Clear .part files when the remote resource is known to have changed
- Confirm the server honors Range requests (curl -H 'Range: bytes=0-0' -> expect 206)
- Treat this error as informational — the next attempt performs a full restart
When it happens
Trigger: Calling download on an output whose .part file exists and is non-empty, and the server responds HTTP 416 to the Range header — e.g. the .part is larger than the (new) file size, the remote file changed between sessions, or the server lost its ability to serve ranges.
Common situations: Resuming a download after the source file was replaced with a smaller version; a .part left over from a different URL written to the same output path; servers/proxies that previously supported ranges but now return 416 (config change, cache purge); clock/mirror switch changing content length mid-resume.
Related errors
- download de {} falhou: HTTP {}
- nao foi possivel buscar {}: {}
- Size mismatch: expected {} bytes, got {}
- Server returned HTML instead of media — the link may have ex
- HTTP {} downloading {}
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/89a350c08a975829.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/direct_downloader.rs:502
if existing_bytes > 0 {
if let Some(total) = total_size {
if existing_bytes >= total {
return Ok(());
}
}
request = request.header("Range", format!("bytes={}-", existing_bytes));
}
let response = request.send().await?;
let mut offset = 0u64;
if existing_bytes > 0 {
if response.status() == reqwest::StatusCode::PARTIAL_CONTENT {
offset = existing_bytes;
} else if response.status() == reqwest::StatusCode::RANGE_NOT_SATISFIABLE {
let _ = std::fs::remove_file(part_path);
return Err(anyhow!("Range not satisfiable, restarting"));
} else if !response.status().is_success() {
return Err(anyhow!("HTTP {} downloading {}", response.status(), url));
}
} else if !response.status().is_success() {
return Err(anyhow!("HTTP {} downloading {}", response.status(), url));
}
if let Some(ct) = response.headers().get("content-type") {
if let Ok(ct_str) = ct.to_str() {
if ct_str.contains("text/html") {
return Err(anyhow!(
"Server returned HTML instead of media — URL may have expired"
));
}
}
}
use std::io::Write;View on GitHub (pinned to 8600b91f42)