cjpais/Handy · error · anyhow::Error

download incomplete: expected {} bytes, got {}

Error message

download incomplete: expected {} bytes, got {}

What it means

The byte stream ended cleanly (Ok(None) from the stream) before the expected number of bytes arrived: the final on-disk length differs from known_total. An early close is treated as a truncated transfer; the partial is removed because a short file has no resume value against a mismatching server. The catalog-hash verification after this check is what makes mirror fallbacks trustworthy.

Source

Thrown at src-tauri/src/managers/model/download.rs:368

                    ));
                }
            }
            file.write_all(&chunk)?;
            downloaded += chunk.len() as u64;
            if last_emit.elapsed() >= throttle {
                emit_progress(downloaded);
                last_emit = Instant::now();
            }
        }
        file.flush()?;
        drop(file);
        emit_progress(downloaded);

        if let Some(expected) = known_total {
            let actual = partial_path.metadata()?.len();
            if actual != expected {
                let _ = fs::remove_file(partial_path);
                return Err(anyhow::anyhow!(
                    "download incomplete: expected {} bytes, got {}",
                    expected,
                    actual
                ));
            }
        }

        // The catalog hash is the trust anchor: for a mirror (an untrusted
        // host) this verification is what makes the fallback safe at all.
        Self::verify_file_with_events(model_id, partial_path, expected_sha256, emit).await?;
        Ok(HttpDownloadOutcome::Completed)
    }
}

#[cfg(test)]
mod tests;

View on GitHub (pinned to 98a4d80cce)

Solutions

  1. Retry — the partial is already deleted, so the next attempt is a clean full download
  2. Check for proxy/VPN/AV interference with large streaming responses
  3. Confirm the server serves the whole file: curl -o /dev/null -w '%{size_download}' <url>
  4. Move to a more reliable source (official HF) for multi-GB models
Defensive patterns

Strategy: retry

Try / catch

match downloader.download_http_resumable(...).await {
    Ok(outcome) => Ok(outcome),
    Err(e) if e.to_string().starts_with("download incomplete") => {
        // partial removed; a retry is a clean full download
        downloader.download_http_resumable(...).await
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Server closes early while Content-Length promised more bytes; proxy or CDN truncation; a connection reset surfaced as clean EOF.

Common situations: Flaky networks dropping the connection mid-body; servers under load closing streams; antivirus or middleboxes cutting large streaming responses.

Related errors


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