tonhowtf/omniget · info

Download cancelled

Error message

Download cancelled

What it means

download_playlist() checks a shared cancellation token before processing each playlist item; if the user (or the app) cancelled the operation, it aborts the whole playlist with 'Download cancelled'. This is a normal control-flow error used to unwind a long-running download, not a malfunction.

Solutions

  1. Treat this error as expected cancellation: catch it and stop without retrying or surfacing as a failure to the user.
  2. If cancellation is unintentional, ensure the cancel token is not shared with another long-lived operation and is reset before starting a new download.
  3. Persist completed item count (success_count) so a retry can resume from where it left off.

Example fix

// before
match download(...).await {
    Err(e) => show_error(e),
    Ok(r) => show_done(r),
}
// after
match download(...).await {
    Err(e) if e.to_string().contains("Download cancelled") => show_cancelled(),
    Err(e) => show_error(e),
    Ok(r) => show_done(r),
}
Defensive patterns

Strategy: try-catch

Try / catch

match download(...).await {
    Err(e) if e.to_string().contains("Download cancelled") => { /* treat as normal stop */ }
    Err(e) => report_error(e),
    Ok(r) => report_done(r),
}

Prevention

When it happens

Trigger: Calling download() on a playlist-type bilibili MediaInfo while opts.cancel_token.is_cancelled() becomes true between playlist items.

Common situations: User clicks Cancel in the UI mid-playlist; the app shuts down and cancels in-flight tasks; a caller drops/short-circuits and cancels the token after a timeout.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/bilibili/legacy.rs:270

async fn download_playlist(
    info: &MediaInfo,
    opts: &DownloadOptions,
    progress: mpsc::Sender<ProgressUpdate>,
    ytdlp_path: &std::path::Path,
) -> anyhow::Result<DownloadResult> {
    let total = info.available_qualities.len().max(1);
    let mut last_result = DownloadResult {
        file_path: opts.output_dir.clone(),
        file_size_bytes: 0,
        duration_seconds: 0.0,
        torrent_id: None,
    };
    let mut success_count: usize = 0;
    let mut first_error: Option<anyhow::Error> = None;

    for (i, quality) in info.available_qualities.iter().enumerate() {
        if opts.cancel_token.is_cancelled() {
            return Err(anyhow!("Download cancelled"));
        }

        let (entry_tx, mut entry_rx) = mpsc::channel::<ProgressUpdate>(16);
        let progress_clone = progress.clone();
        let total_f = total as f64;
        let idx = i as f64;

        tokio::spawn(async move {
            while let Some(p) = entry_rx.recv().await {
                let overall = (idx + p.percent / 100.0) / total_f * 100.0;
                let _ = progress_clone
                    .send(ProgressUpdate::rich(overall, None, None, p.speed_bps, None))
                    .await;
            }
        });

        let extra = vec!["--no-playlist".to_string()];

View on GitHub (pinned to 8600b91f42)