{"record":{"id":"de876064ad0abe6f","repo":"ducaale/xh","slug":"incomplete-download-size-downloaded","errorCode":null,"errorMessage":"Incomplete download: size={}; downloaded={}","messagePattern":"Incomplete download: size=(.+?); downloaded=(.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/download.rs","lineNumber":293,"sourceCode":"        let time_taken = starting_time.elapsed();\n        if !time_taken.is_zero() {\n            eprintln!(\n                \"{verb}. {} in {:.5}s ({}/s)\",\n                HumanBytes(downloaded_length),\n                time_taken.as_secs_f64(),\n                HumanBytes((downloaded_length as f64 / time_taken.as_secs_f64()) as u64)\n            );\n        } else {\n            eprintln!(\"{verb}. {}\", HumanBytes(downloaded_length));\n        }\n        if incomplete.is_some() {\n            // Separate the summary from the error message that follows.\n            eprintln!();\n        }\n    }\n\n    if let Some(total_length) = incomplete {\n        return Err(anyhow!(\n            \"Incomplete download: size={}; downloaded={}\",\n            total_length,\n            total_downloaded_length\n        ));\n    }\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn content_range_parsing() {\n        let expected = vec![\n            (2, \"bytes 2-5/6\", Some(6)),\n            (2, \"bytes 2-5/*\", Some(6)),","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/ducaale/xh/blob/2404aceecc08b0b2d100fedc96f57745cd5904dc/src/download.rs#L275-L311","documentation":"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.","triggerScenarios":"The response body stream terminates early (connection closed, timeout, server truncation) so total_downloaded_length < total_length at the end of download_file.","commonSituations":"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.","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."],"exampleFix":"// before\nlet result = download_file(url, None);\n// after\nlet result = match download_file(url, saved_offset) {\n    Err(e) if is_incomplete(&e) => download_file(url, saved_offset_from_partial),\n    r => r,\n};","handlingStrategy":"retry","validationCode":"let expected = content_length_response_size_estimate; // from headers\nlet downloaded = std::fs::metadata(partial_path)?.len();\nif downloaded < expected { /* resume before declaring success */ }","typeGuard":null,"tryCatchPattern":"loop {\n    match download_file(url, current_offset()) {\n        Err(e) if e.to_string().contains(\"Incomplete download\") && retries < 3 => { retries += 1; continue; }\n        other => break other,\n    }\n}","preventionTips":["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."],"tags":["download","network","truncated","http"],"backgroundTag":"http-request-failed","analyzedSha":"2404aceecc08b0b2d100fedc96f57745cd5904dc","analyzedAt":"2026-09-13T19:13:33.814Z","contentChangedAt":"2026-09-13T19:13:33.814Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}