ducaale/xh · error
Incomplete download: size=
Error message
Incomplete download: size={}; downloaded={} What it means
After the download stream ends, download_file compares the bytes actually written against the expected total length. If fewer bytes were received than the server promised, the download is declared incomplete and this error is raised.
Solutions
- Retry the download using the resume option so it continues from the last byte received.
- Improve network stability or retry with a longer/robust connection configuration.
- Check whether a proxy or firewall is cutting long-lived connections; adjust its idle timeout.
- Verify free disk space and that the target file is writable, then resume again.
Example fix
// before
let result = download_file(url, None);
// after
let result = match download_file(url, saved_offset) {
Err(e) if is_incomplete(&e) => download_file(url, saved_offset_from_partial),
r => r,
}; Defensive patterns
Strategy: retry
Validate before calling
let expected = content_length_response_size_estimate; // from headers
let downloaded = std::fs::metadata(partial_path)?.len();
if downloaded < expected { /* resume before declaring success */ } Try / catch
loop {
match download_file(url, current_offset()) {
Err(e) if e.to_string().contains("Incomplete download") && retries < 3 => { retries += 1; continue; }
other => break other,
}
} Prevention
- Implement automatic resume on incomplete downloads, up to a retry cap.
- Persist the number of bytes received after each chunk so resume is exact.
- Avoid killing the process mid-transfer; handle SIGINT to flush offsets.
- Check disk space before starting large downloads.
When it happens
Trigger: The response body stream terminates early (connection closed, timeout, server truncation) so total_downloaded_length < total_length at the end of download_file.
Common situations: Flaky Wi-Fi or mobile connections dropping mid-transfer; server/proxy idle timeouts killing long downloads; disk-full is usually a separate error but connection resets are the typical cause; very large files over unstable links.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Can't parse Content-Range header, can't resume download
- Invalid Content-Range
- Content-Range has wrong end
- Content-Range has wrong start
- Missing Content-Range header
AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13).
Data as JSON: /api/errors/de876064ad0abe6f.
Report an issue: GitHub.
Appendix: source
Thrown at src/download.rs:293
let time_taken = starting_time.elapsed();
if !time_taken.is_zero() {
eprintln!(
"{verb}. {} in {:.5}s ({}/s)",
HumanBytes(downloaded_length),
time_taken.as_secs_f64(),
HumanBytes((downloaded_length as f64 / time_taken.as_secs_f64()) as u64)
);
} else {
eprintln!("{verb}. {}", HumanBytes(downloaded_length));
}
if incomplete.is_some() {
// Separate the summary from the error message that follows.
eprintln!();
}
}
if let Some(total_length) = incomplete {
return Err(anyhow!(
"Incomplete download: size={}; downloaded={}",
total_length,
total_downloaded_length
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn content_range_parsing() {
let expected = vec![
(2, "bytes 2-5/6", Some(6)),
(2, "bytes 2-5/*", Some(6)),View on GitHub (pinned to 2404aceecc)