tonhowtf/omniget · info · anyhow::Error

Download cancelled

Error message

Download cancelled

What it means

Thrown in download_streaming when the fetcher's cancel token (self.cancel) is observed to be cancelled, checked at the top of each chunk-read loop iteration. This is the library's cooperative cancellation mechanism: it lets a caller abort an in-flight download and returns a distinct, expected error instead of panicking or leaking the task. The .part file is left on disk (resume data may also be retained depending on use_sidecar_resume).

Solutions

  1. This error signals intentional cancellation — handle it as a control-flow signal, not a failure: match on the message/cancel cause and skip alerting the user.
  2. If cancellation was unintended, audit who calls .cancel() on the token (timeout wrappers, UI handlers) and fix the trigger.
  3. If downloads should resume after cancellation, keep the .part file and re-invoke download with resume/sidecar support enabled.
  4. If you want the error programmatically distinguishable, wrap the call and map this specific message to your own Cancelled error variant.

Example fix

// before
fetcher.download(&mut progress_tx).await?; // treats cancel as a hard error

// after
match fetcher.download(&mut progress_tx).await {
    Err(e) if e.to_string().contains("Download cancelled") => {
        tracing::info!("download cancelled by user");
    }
    other => other?,
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the token is fresh before starting
if cancel_token.is_cancelled() {
    bail!("not starting download: token already cancelled");
}

Try / catch

match fetcher.download(&mut tx).await {
    Err(e) if e.to_string().contains("Download cancelled") => {
        // expected control flow: clean up UI state, keep .part for resume
        set_download_state(DownloadState::Cancelled);
    }
    other => other?,
}

Prevention

When it happens

Trigger: A CancellationToken was passed to the fetcher and .cancel() was called (e.g. user pressed a Cancel button, app shutdown, or a supervisor timed the task out) while download_streaming was between chunks.

Common situations: User-initiated cancel in a GUI (Tauri app) mid-download; application shutdown or window close triggering token cancellation; a new download superseding an old one; watchdog cancelling a stalled download.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/http_fetcher.rs:327

        let resp = req.send().await?;
        if !resp.status().is_success() {
            return Err(anyhow!("HTTP {} downloading {}", resp.status(), self.url));
        }
        let total = header_content_length(resp.headers()).or_else(|| resp.content_length());

        let mut file = tokio::fs::File::create(part_path).await?;
        let mut downloaded: u64 = 0;
        let mut stream = resp.bytes_stream();

        let mut last_emit = std::time::Instant::now();
        let mut anchor_bytes = 0u64;
        let mut anchor_time = std::time::Instant::now();
        let mut speed_ema: f64 = 0.0;

        loop {
            if let Some(token) = &self.cancel {
                if token.is_cancelled() {
                    return Err(anyhow!("Download cancelled"));
                }
            }
            match tokio::time::timeout(self.config.read_timeout, stream.next()).await {
                Ok(Some(Ok(chunk))) => {
                    file.write_all(&chunk).await?;
                    downloaded += chunk.len() as u64;
                    if last_emit.elapsed() >= Duration::from_millis(250) {
                        let elapsed = anchor_time.elapsed().as_secs_f64();
                        if elapsed >= 0.3 {
                            let instant =
                                (downloaded.saturating_sub(anchor_bytes)) as f64 / elapsed;
                            speed_ema = if speed_ema > 0.0 {
                                speed_ema * 0.6 + instant * 0.4
                            } else {
                                instant
                            };
                            anchor_bytes = downloaded;
                            anchor_time = std::time::Instant::now();

View on GitHub (pinned to 8600b91f42)