Zackriya-Solutions/meetily · error · anyhow::Error

Download worker ended before completing cancellation cleanup

Error message

Download worker ended before completing cancellation cleanup

What it means

During cancel_download cleanup, the engine subscribes to the download worker's completion watch channel. If completion.changed() returns Err, it means the watch channel's sender was dropped — i.e., the download worker task terminated without ever setting the completion flag, so cancellation cleanup can never be confirmed.

Source

Thrown at frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs:1220

        &self,
        model_name: &str,
        cleanup_timeout: Duration,
    ) -> Result<CancelDownloadOutcome> {
        let active_download = {
            let active_downloads = self.active_downloads.lock().await;
            let Some(active_download) = active_downloads.downloads.get(model_name).cloned() else {
                return Ok(CancelDownloadOutcome::Cancelled);
            };
            active_download.cancellation.cancel();
            active_download
        };

        let mut completion = active_download.completion.subscribe();
        if !*completion.borrow() {
            match timeout(cleanup_timeout, completion.changed()).await {
                Ok(Ok(())) => {}
                Ok(Err(_)) => {
                    return Err(anyhow!(
                        "Download worker ended before completing cancellation cleanup"
                    ));
                }
                Err(_) => return Ok(CancelDownloadOutcome::Pending),
            }
        }

        Ok(CancelDownloadOutcome::Cancelled)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crossbeam::queue::SegQueue;
    use tempfile::tempdir;
    use tokio::io::{AsyncReadExt, AsyncWriteExt};
    use tokio::net::TcpListener;

View on GitHub (pinned to a2cb62e827)

Solutions

  1. Retry the cancel operation; if the outcome is still broken, restart the app to reset download worker state.
  2. Check Rust logs for a panic or abort in the download worker task to identify the root cause.
  3. Ensure the worker sets completion=true (or drops the sender only after setting it) in all exit paths, including error paths.
  4. Reduce race windows: only trigger cancel after the download has started reporting progress, or make the worker's completion channel a shutdown-safe signal.

Example fix

// before
Ok(Err(_)) => {
    return Err(anyhow!("Download worker ended before completing cancellation cleanup"));
}
// after (treat closed channel as cleanup finished if the worker exited deliberately)
Ok(Err(_)) => {
    if worker_join_handle.is_finished() {
        return Ok(CancelDownloadOutcome::Cancelled);
    }
    return Err(anyhow!("Download worker ended before completing cancellation cleanup"));
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before cancelling, check the worker is still alive
if worker_handle.is_finished() {
    eprintln!("worker already exited; skip cleanup wait");
}

Type guard

// Rust: check the watch channel is still connected before waiting
if active_download.completion.sender().is_some() {
    // safe to await completion.changed()
}

Try / catch

// Rust
match timeout(cleanup_timeout, completion.changed()).await {
    Ok(Ok(())) => Ok(CancelDownloadOutcome::Cancelled),
    Ok(Err(_)) => {
        log::warn!("completion sender dropped; treating as worker exit");
        Ok(CancelDownloadOutcome::Cancelled) // or surface to user with retry hint
    }
    Err(_) => Ok(CancelDownloadOutcome::Pending),
}

Prevention

When it happens

Trigger: In cancel_download, while waiting on active_download.completion.changed() within cleanup_timeout, the watch sender is dropped because the download worker task panicked, was aborted, or exited early without marking completion — the Ok(Err(_)) branch.

Common situations: The download worker panicked mid-download (e.g., a bug or OOM kill of the task); the task holding the watch sender was aborted after cancellation; the worker exited early on an unhandled error path without setting completion=true, leaving the cleanup watcher observing a closed channel.

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 Zackriya-Solutions/meetily@a2cb62e827 (2026-09-12). Data as JSON: /api/errors/674d328861e1df85. Report an issue: GitHub.