cjpais/Handy · warning · anyhow::Error

transfer stalled: no progress for

Error message

transfer stalled: no progress for {}s

What it means

This is an internal, recoverable watchdog signal, not a terminal failure: hf-hub has no internal timeouts, so the manager wraps each attempt with a child cancel token that fires when no download progress is observed within DOWNLOAD_STALL_TIMEOUT (60s, model/download.rs:26). The library surfaces the cut connection as ApiError::Cancelled; when the parent (user) token is NOT cancelled, the code maps it to this synthetic stall error. The retry loop then backs off (1s << attempt) and retries with fewer parallel streams, falling back to a mirror after a sequential stall.

Solutions

  1. On first occurrence, do nothing — the built-in backoff, single-stream retry, and mirror fallback handle it
  2. If stalls recur: switch to a stable network or disable VPN/proxy and retry the download
  3. Retry the same model later — the shared .sync.part cache resumes instead of restarting
  4. As a maintainer: do not add extra retries beyond the schedule; a sequential stall already costs 60s and the code correctly diverts to the mirror instead
Defensive patterns

Strategy: retry

Try / catch

// internal to the manager: map ApiError::Cancelled to stall only when the user didn't cancel
Err(hf_hub::api::tokio::ApiError::Cancelled) if !cancel_token.is_cancelled() => {
    // already handled: back off (1s << attempt), reduce streams, or break to mirror fallback
}

Prevention

When it happens

Trigger: A wedged TCP connection (NAT/proxy silently dropping bytes) while downloading with parallel range streams; server-side throttling that halts one stream; laptop sleep/resume mid-download; a flaky VPN or hotspot link that stalls without resetting.

Common situations: Unstable Wi-Fi or mobile hotspot; VPN/proxy middleboxes idling out large transfers; HF CDN hiccups under load; machine suspending during a multi-GB model download.

Related errors


AI-assisted analysis of cjpais/Handy@ba10ce1943 (2026-08-17). Data as JSON: /api/errors/7db18e3fbe5e8668. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/managers/model.rs:2042

                        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 ba10ce1943)