tonhowtf/omniget · error
Download failed after {} attempts
Error message
Download failed after {} attempts What it means
This is the terminal error thrown by download_direct_with_headers after the retry loop has exhausted MAX_RETRIES attempts. Every individual attempt failed (network errors, HTTP errors, size mismatches, etc.), last_err holds the final underlying error, and the .part partial file is deleted before returning. If no attempt recorded an error, this generic anyhow! fallback is used instead.
Solutions
- Read the chained source error (last_err) logged alongside this message — it names the real per-attempt failure; fix that root cause first.
- Verify the URL is reachable with curl -I <url> from the same machine to rule out network/proxy issues.
- Increase retry tolerance around the call (or re-call download later) if the server is intermittently available; check MAX_RETRIES in direct_downloader.rs if the default is too small for your environment.
- Check for proxy/VPN/firewall interference and configure reqwest's proxy settings if the environment requires one.
- If the server consistently fails, confirm the link is still valid and not expired or login-gated (see the HTML-masquerade error).
Example fix
// before
let result = downloader.download(&url, &out).await?; // bubbles 'Download failed after N attempts'
// after
match downloader.download(&url, &out).await {
Ok(p) => println!("downloaded to {}", p.display()),
Err(e) if e.to_string().contains("Download failed after") => {
eprintln!("transient network failure: {e:#}; will retry later");
// surface the root cause: {e:#} prints the per-attempt source error
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: retry
Validate before calling
// Rust: pre-flight reachability check before calling download
async fn url_reachable(client: &reqwest::Client, url: &str) -> bool {
matches!(client.head(url).send().await, Ok(r) if r.status().is_success())
} Try / catch
match downloader.download(&url, &out).await {
Ok(path) => Ok(path),
Err(e) if e.to_string().contains("Download failed after") => {
// e's source chain holds the last per-attempt error; log with {e:#}
Err(anyhow!("download unavailable after retries: {e:#}"))
}
Err(e) => Err(e),
} Prevention
- HEAD-check the URL before starting a long download
- Run downloads only on networks without intercepting proxies/captive portals
- Tune MAX_RETRIES/upstream backoff for known-flaky endpoints
- Always log the full anyhow error chain ({e:#}) to see the real per-attempt cause
When it happens
Trigger: Calling download/download_direct/download_direct_with_headers on a URL that fails on every one of MAX_RETRIES consecutive attempts — e.g. persistent connection resets, a server that returns 5xx on every request, or a TLS handshake failure — so the retry loop exits without success.
Common situations: Target server is down or overloaded; corporate proxy or firewall silently drops the connection; DNS resolves but the host is unreachable; flaky mobile/hotel Wi-Fi; the URL points to a host that rate-limits or blacklists the client; MAX_RETRIES too low for a genuinely slow endpoint.
Related errors
- download de {} falhou: HTTP {}
- nao foi possivel buscar {}: {}
- HTTP {} downloading {}
- Download timeout — no data received for 30 seconds
- Failed to download yt-dlp: HTTP {}
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/657a068282785fc5.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/direct_downloader.rs:138
}
if is_fatal_error(&e) {
let _ = std::fs::remove_file(&part_path_for(output));
return Err(e);
}
tracing::warn!(
"[direct] attempt {}/{} failed: {}",
attempt + 1,
MAX_RETRIES,
e
);
last_err = Some(e);
attempt += 1;
}
}
}
let _ = std::fs::remove_file(&part_path_for(output));
Err(last_err.unwrap_or_else(|| anyhow!("Download failed after {} attempts", MAX_RETRIES)))
}
fn part_path_for(output: &Path) -> PathBuf {
let mut part = output.as_os_str().to_owned();
part.push(".part");
PathBuf::from(part)
}
fn is_fatal_error(err: &anyhow::Error) -> bool {
let msg = err.to_string();
for code in &[
"HTTP 400", "HTTP 401", "HTTP 403", "HTTP 404", "HTTP 405", "HTTP 410", "HTTP 451",
] {
if msg.contains(code) {
return true;
}
}
if msg.contains("HTML instead of media") {View on GitHub (pinned to 8600b91f42)