libnyanpasu/clash-nyanpasu · error
failed to start download: {e}
Error message
failed to start download: {e} What it means
Wraps a failure of the download task's `run()` call in `DownloadSession::start`. `run()` initiates the transfer; if the underlying downloader cannot even begin (runtime submit failure, task already finished/cancelled, adapter setup error), the error is re-wrapped as "failed to start download: {e}". This is distinct from a mid-transfer failure, which surfaces later as "download failed".
Source
Thrown at backend/tauri/src/core/download/mod.rs:128
.adapter(adapter)
.save_path(save_path)
.threaded_runtime(ThreadedRuntimeImpl::new_tokio_rt())
.cancel_token(inner.cancel.clone())
.on_task_state_changed(move |event| cb_inner.on_event(event))
.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,
}
}View on GitHub (pinned to f7dbce2997)
Solutions
- Create a fresh `DownloadSession` for each download instead of re-running a finished/failed one.
- Do not call `start()` after `cancel()` or after `status()` reports a terminal state; check status first.
- Read the wrapped `{e}` to identify the underlying cause (already-run, cancelled, runtime failure).
- If transient (runtime spawn failure), retry after freeing resources.
Example fix
// before
session.start().await?;
// after
if let DownloadSession::Failed(_) = session.status().state {
anyhow::bail!("previous download failed; create a new session");
}
session.start().await.map_err(|e| anyhow!("cannot start download: {e:#}"))?; Defensive patterns
Strategy: validation
Validate before calling
// check session state before starting
if let DownloaderState::Failed(_) | DownloaderState::Finished = *session.status().state {
anyhow::bail!("session already terminal; create a new DownloadSession");
} Try / catch
session.start().await
.map_err(|e| { log::error!("download could not start: {e:#}"); e })?; Prevention
- Use one DownloadSession per download; never call start() twice on the same session
- Do not start after cancel(); check status() first
- Ensure the tokio runtime is alive and not saturated
- Treat a start failure as a session-lifecycle bug, not a network issue
When it happens
Trigger: Calling `DownloadSession::start()` and the task's `run()` future returns an error — e.g. starting a task that was already run/completed, the cancel token already fired, or the threaded runtime rejects the spawn.
Common situations: Calling `start()` twice on the same session; calling start after `cancel()`; resource-exhaustion in the runtime; bug in session reuse after a previous terminal state.
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
- legacy verge bridge is not managed
- failed to build download task: {e}
- download failed: {e}
- unimplemented!()
- IPC server is already initialized
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/957065f7430eff79.
Report an issue: GitHub.