libnyanpasu/clash-nyanpasu · error

download failed: {e}

Error message

download failed: {e}

What it means

Returned by `DownloadSession::start` when the task was started successfully but `task.wait()` later reports the transfer failed. Before returning, the session's state is set to `DownloaderState::Failed(e.to_string())`, so the failure reason is persisted in `status()` as well. This is the mid-transfer failure path: network errors, server errors, checksum/IO failures during download.

Source

Thrown at backend/tauri/src/core/download/mod.rs:133

            .build()
            .await
            .map_err(|e| anyhow::anyhow!("failed to build download task: {e}"))?;
        Ok(Self {
            inner,
            task: Mutex::new(task),
        })
    }

    pub async fn start(&self) -> anyhow::Result<()> {
        let mut task = self.task.lock().await;
        task.run()
            .await
            .map_err(|e| anyhow::anyhow!("failed to start download: {e}"))?;
        match task.wait().await {
            Ok(()) => Ok(()),
            Err(e) => {
                *self.inner.state.write() = DownloaderState::Failed(e.to_string());
                Err(anyhow::anyhow!("download failed: {e}"))
            }
        }
    }

    pub fn status(&self) -> DownloadStatus {
        let prog = *self.inner.progress.read();
        DownloadStatus {
            state: self.inner.state.read().clone(),
            downloaded: prog.downloaded,
            total: prog.total,
            speed: prog.speed,
        }
    }

    #[allow(dead_code)]
    pub fn cancel(&self) {
        self.inner.cancel.cancel();
    }

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Inspect `{e}` and `status()` to learn the failure cause; fix the specific issue (URL, network, disk).
  2. Retry the download with a new session, ideally with backoff for transient network/server errors.
  3. Verify the URL is reachable (curl -I) and the target still exists (core binaries move between releases).
  4. Check free disk space and write permissions on save_path.
  5. If behind a proxy/firewall, configure the reqwest client's proxy settings.

Example fix

// before
session.start().await?;
// after
match session.start().await {
    Ok(()) => {}
    Err(e) => {
        log::warn!("download failed: {e:#}, retrying...");
        tokio::time::sleep(Duration::from_secs(5)).await;
        let session = DownloadSession::new(client, url, save_path).await?;
        session.start().await?;
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight the URL before downloading
let head = client.head(url.as_str()).send().await?;
if !head.status().is_success() {
    anyhow::bail!("URL unreachable: HTTP {}", head.status());
}

Try / catch

match session.start().await {
    Ok(()) => Ok(()),
    Err(e) if is_transient(&e) => retry_with_backoff(|| async {
        DownloadSession::new(client.clone(), url.clone(), save_path.clone()).await?
            .start().await
    }),
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling `start()` and the download ultimately fails — HTTP errors from the server (4xx/5xx), connection resets/timeouts, disk full or write error while saving, cancelled-by-remote, or any error surfaced by the download crate's `wait()`.

Common situations: Unstable network or VPN drops mid-download; GitHub/CDN returning 403/404/502 for core or geo-data downloads; disk quota exceeded; server closing connection before completion; TLS/proxy interception.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/d7062d1a882c2549. Report an issue: GitHub.