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

  1. Read the chained source error (last_err) logged alongside this message — it names the real per-attempt failure; fix that root cause first.
  2. Verify the URL is reachable with curl -I <url> from the same machine to rule out network/proxy issues.
  3. 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.
  4. Check for proxy/VPN/firewall interference and configure reqwest's proxy settings if the environment requires one.
  5. 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

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


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)