{"record":{"id":"657a068282785fc5","repo":"tonhowtf/omniget","slug":"download-failed-after-attempts","errorCode":null,"errorMessage":"Download failed after {} attempts","messagePattern":"Download failed after (.+?) attempts","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/direct_downloader.rs","lineNumber":138,"sourceCode":"                }\n                if is_fatal_error(&e) {\n                    let _ = std::fs::remove_file(&part_path_for(output));\n                    return Err(e);\n                }\n                tracing::warn!(\n                    \"[direct] attempt {}/{} failed: {}\",\n                    attempt + 1,\n                    MAX_RETRIES,\n                    e\n                );\n                last_err = Some(e);\n                attempt += 1;\n            }\n        }\n    }\n\n    let _ = std::fs::remove_file(&part_path_for(output));\n    Err(last_err.unwrap_or_else(|| anyhow!(\"Download failed after {} attempts\", MAX_RETRIES)))\n}\n\nfn part_path_for(output: &Path) -> PathBuf {\n    let mut part = output.as_os_str().to_owned();\n    part.push(\".part\");\n    PathBuf::from(part)\n}\n\nfn is_fatal_error(err: &anyhow::Error) -> bool {\n    let msg = err.to_string();\n    for code in &[\n        \"HTTP 400\", \"HTTP 401\", \"HTTP 403\", \"HTTP 404\", \"HTTP 405\", \"HTTP 410\", \"HTTP 451\",\n    ] {\n        if msg.contains(code) {\n            return true;\n        }\n    }\n    if msg.contains(\"HTML instead of media\") {","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/direct_downloader.rs#L120-L156","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nlet result = downloader.download(&url, &out).await?; // bubbles 'Download failed after N attempts'\n// after\nmatch downloader.download(&url, &out).await {\n    Ok(p) => println!(\"downloaded to {}\", p.display()),\n    Err(e) if e.to_string().contains(\"Download failed after\") => {\n        eprintln!(\"transient network failure: {e:#}; will retry later\");\n        // surface the root cause: {e:#} prints the per-attempt source error\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"retry","validationCode":"// Rust: pre-flight reachability check before calling download\nasync fn url_reachable(client: &reqwest::Client, url: &str) -> bool {\n    matches!(client.head(url).send().await, Ok(r) if r.status().is_success())\n}","typeGuard":null,"tryCatchPattern":"match downloader.download(&url, &out).await {\n    Ok(path) => Ok(path),\n    Err(e) if e.to_string().contains(\"Download failed after\") => {\n        // e's source chain holds the last per-attempt error; log with {e:#}\n        Err(anyhow!(\"download unavailable after retries: {e:#}\"))\n    }\n    Err(e) => Err(e),\n}","preventionTips":["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"],"tags":["network","download","retry","rust"],"backgroundTag":"network-request-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"}