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

  1. Let the built-in retry ladder run — it drops from 4 concurrent streams to 1 with exponential backoff, then mirrors
  2. Stabilize the network (disable VPN, switch to wired/another network) before retrying
  3. If stalls repeat at the same byte offset, the resume .sync.part may be wedged server-side — cancel and restart the download once
  4. 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

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


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