cjpais/Handy · warning · anyhow::Error
transfer stalled: no progress for {}s
Error message
transfer stalled: no progress for {}s What it means
The download watchdog cancelled the hf-hub attempt because no transfer progress occurred for DOWNLOAD_STALL_TIMEOUT (60 s) — hf-hub has no internal timeouts, so a wedged connection would otherwise hang forever. The code distinguishes this from a user cancel by checking cancel_token afterwards; this branch is the watchdog path. It is recoverable by design: the loop retries with fewer concurrent streams or falls back to a mirror.
Source
Thrown at src-tauri/src/managers/model.rs:1997
Err(_) => Err(hf_hub::api::tokio::ApiError::Cancelled),
}
}
};
watchdog.abort();
match result {
Ok(_) => break None,
Err(hf_hub::api::tokio::ApiError::Cancelled) if cancel_token.is_cancelled() => {
// User cancelled. hf-hub leaves the partially downloaded
// `.sync.part` in the shared cache, so a later attempt resumes
// instead of restarting. The guard resets is_downloading and
// drops the token; `cancel_download` already emitted
// `model-download-cancelled`.
info!("HF download cancelled for: {}", model_id);
return Ok(());
}
Err(hf_hub::api::tokio::ApiError::Cancelled) => {
let err = anyhow::anyhow!(
"transfer stalled: no progress for {}s",
DOWNLOAD_STALL_TIMEOUT.as_secs()
);
// A parallel attempt may be what wedged the network. Give
// the connection pool a brief pause, then retry once using
// the known-compatible single-stream path. A sequential
// stall already cost DOWNLOAD_STALL_TIMEOUT, so further
// retries would likely just repeat it — use the mirror.
if stream_count == 1 || attempt >= ATTEMPT_STREAMS.len() {
break Some(err);
}
let delay = Duration::from_secs(1_u64 << attempt);
warn!(
"HF download attempt {}/{} stalled for {} using {} concurrent stream(s); retrying with {} stream(s) in {}s",
attempt,
ATTEMPT_STREAMS.len(),
model_id,
stream_count,View on GitHub (pinned to 98a4d80cce)
Solutions
- Let the built-in retry ladder run — it drops from 4 concurrent streams to 1 with exponential backoff, then mirrors
- Stabilize the network (disable VPN, switch to wired/another network) before retrying
- If stalls repeat at the same byte offset, the resume .sync.part may be wedged server-side — cancel and restart the download once
- Check for bandwidth throttling or proxy interference on Range requests
Defensive patterns
Strategy: retry
Try / catch
// the loop already handles this: verify cancel_token first, then retry with fewer streams or break to mirrors
Err(hf_hub::api::tokio::ApiError::Cancelled) if !cancel_token.is_cancelled() => {
warn!("stall watchdog fired; retrying single-stream or falling back to mirror");
} Prevention
- Keep DOWNLOAD_STALL_TIMEOUT >= worst-case expected inter-chunk latency on slow links (currently 60 s)
- Prefer stable networks for multi-GB model downloads; the watchdog cannot fix a dead path, only detect it
- Leave the resume .sync.part intact on cancel — later attempts continue instead of restarting
When it happens
Trigger: Connection silently drops mid-stream (NAT/VPN idle timeout, captive portal re-auth, switching networks); server or CDN stops sending bytes but keeps the socket open; a throttling proxy that freezes > 60 s. Occurs only during multi-stream or single-stream HF downloads of model files.
Common situations: Hotel/coffee-shop Wi-Fi with flaky NAT; VPN reconnects mid-download; corporate proxies that stall large Range-request transfers; long model downloads (hundreds of MB to GB) that outlive flaky paths.
Related errors
- {:?}
- Hugging Face download failed after {} attempt(s): {}
- no response within {}s from {}
- Download failed from Hugging Face ({}) and {} mirror(s)
- transfer stalled: no data for {}s
AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16).
Data as JSON: /api/errors/7db18e3fbe5e8668.
Report an issue: GitHub.