tonhowtf/omniget · info

Download cancelled

Error message

Download cancelled

What it means

download_direct_with_headers checks the caller-supplied CancellationToken at the top of each retry attempt and aborts immediately with this error when the token is already cancelled. It is the library's cooperative-cancellation mechanism for direct (non-aria2c) downloads, including mid-download retries.

Solutions

  1. Treat this error as an expected, non-fatal signal: stop and clean up without alerting the user
  2. Check token.is_cancelled() before calling download_direct to skip no-op work
  3. Use tokio::select! on the download future vs token.cancelled() for immediate cancellation even mid-request
  4. If cancellation was unintentional, verify the token lifecycle — tokens are not resettable; create a fresh CTS per download

Example fix

// before
match download_direct(url, Some(&token)).await {
    Err(e) => return Err(e), // treats cancel like a hard failure
    Ok(f) => f,
}
// after
match download_direct(url, Some(&token)).await {
    Err(e) if format!("{}", e).contains("Download cancelled") => {
        tracing::info!("download cancelled by user"); // expected path
    }
    Err(e) => return Err(e),
    Ok(f) => f,
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check before invoking to avoid a guaranteed error return
if token.as_ref().map(|t| t.is_cancelled()).unwrap_or(false) {
    eprintln!("download already cancelled; skipping call");
    return;
}

Try / catch

tokio::select! {
    res = download_direct_with_headers(url, headers, Some(&token)) => match res {
        Err(e) if e.to_string().contains("Download cancelled") => {
            tracing::info!("download cancelled by user"); // expected, non-fatal
        }
        other => other?,
    },
    _ = token.cancelled() => tracing::info!("cancelled via token"),
}

Prevention

When it happens

Trigger: The caller cancels the CancellationTokenSource (user pressed stop, UI closed, timeout policy) while download_direct_with_headers is between attempts or before the first request; on the next loop iteration token.is_cancelled() returns true and this error is returned.

Common situations: User cancels a file download in the app UI; a supervisor cancels the token after a global timeout; app shutdown cancels in-flight downloads; retry loop observes cancellation before issuing the next retry request.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/7c341c04eb03e64d. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/direct_downloader.rs:80

    progress_tx: mpsc::Sender<ProgressUpdate>,
    headers: Option<reqwest::header::HeaderMap>,
    cancel: Option<&CancellationToken>,
) -> anyhow::Result<u64> {
    let mut last_err = None;
    // Two independent budgets, both monotonic, so the loop always terminates:
    // `attempt` counts the ordinary retries and `forbidden_retries` counts the
    // 403 ladder. The ladder gets its own counter so a couple of transient
    // network failures cannot eat the escalation steps before they run, and it
    // is a local — the count is per download request, never process-wide.
    let mut attempt: u32 = 0;
    let mut forbidden_retries: u32 = 0;
    let mut requests_made: u32 = 0;
    let mut effective_headers = headers;

    while attempt < MAX_RETRIES {
        if let Some(token) = cancel {
            if token.is_cancelled() {
                return Err(anyhow!("Download cancelled"));
            }
        }

        if requests_made > 0 {
            let base = 1000 * (requests_made as u64);
            let jitter = rand::random::<u64>() % (base / 2 + 1);
            tokio::time::sleep(Duration::from_millis(base + jitter)).await;
        }

        requests_made += 1;
        match download_attempt(
            client,
            url,
            output,
            &progress_tx,
            effective_headers.clone(),
            cancel,
        )

View on GitHub (pinned to 8600b91f42)