tonhowtf/omniget · info · anyhow::Error

Download cancelled by user

Error message

Download cancelled by user

What it means

download_with_quality checks the caller-supplied CancellationToken before doing any work and immediately bails if cancellation was already requested. The library surfaces cooperative cancellation as a regular anyhow error rather than a panic or silent no-op, so callers can distinguish user-aborted downloads from network failures.

Solutions

  1. Treat this error as an expected outcome: catch it and skip retry logic, since the user asked to cancel.
  2. If the download should proceed, create a fresh (non-cancelled) CancellationToken for the attempt instead of reusing an already-cancelled one.
  3. Check token.is_cancelled() before spawning the download task to avoid starting work that will be rejected.
  4. If the cancel was accidental (e.g. watchdog fired early), fix the cancellation trigger's timing before re-invoking download().

Example fix

// before
let token = shared_token.clone();
shared_token.cancel(); // watchdog fired earlier
hls.download(url, quality, token).await?; // -> "Download cancelled by user"
// after
let token = CancellationToken::new(); // fresh token for a new attempt
hls.download(url, quality, token).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

if cancel_token.is_cancelled() {
    // skip starting the download at all
    return;
}

Try / catch

match hls.download(url, quality, token.clone()).await {
    Ok(res) => handle_result(res),
    Err(e) if e.to_string() == "Download cancelled by user" => {
        // expected: no retry, update UI to 'cancelled'
    },
    Err(e) => retry_or_report(e),
}

Prevention

When it happens

Trigger: Calling download() -> download_with_quality with a CancellationToken on which cancel() was already invoked (e.g. the UI fired a cancel while the task was still being spawned), or a token that some other component cancelled before the download started.

Common situations: User clicks Cancel in the app at nearly the same moment the download task is enqueued; a timeout watchdog cancels the token just before the worker starts; reusing a single token across sequential downloads without resetting it.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:143

            None,
        )
        .await
    }

    #[allow(clippy::too_many_arguments)]
    pub async fn download_with_quality(
        &self,
        m3u8_url: &str,
        output_path: &str,
        referer: &str,
        bytes_tx: Option<UnboundedSender<u64>>,
        cancel_token: CancellationToken,
        max_concurrent: u32,
        max_retries: u32,
        max_height: Option<u32>,
    ) -> anyhow::Result<HlsDownloadResult> {
        if cancel_token.is_cancelled() {
            anyhow::bail!("Download cancelled by user");
        }

        let m3u8_text = self.fetch_m3u8_with_retry(m3u8_url, referer, 3).await?;

        let m3u8_bytes = m3u8_text.as_bytes();

        if let Ok((_, master)) = parse_master_playlist(m3u8_bytes) {
            if let Some(variant) = select_best_variant(&master, max_height) {
                let available: Vec<u64> = master
                    .variants
                    .iter()
                    .filter(|v| !v.is_i_frame)
                    .map(variant_height)
                    .collect();
                tracing::info!(
                    "[hls] variant selected: {}p (requested: {}, available: {:?})",
                    variant_height(variant),
                    max_height

View on GitHub (pinned to 8600b91f42)