{"record":{"id":"f6557ba08d2c6cc8","repo":"cjpais/Handy","slug":"download-incomplete-expected-bytes-got","errorCode":null,"errorMessage":"download incomplete: expected {} bytes, got {}","messagePattern":"download incomplete: expected (.+?) bytes, got (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model/download.rs","lineNumber":368,"sourceCode":"                    ));\n                }\n            }\n            file.write_all(&chunk)?;\n            downloaded += chunk.len() as u64;\n            if last_emit.elapsed() >= throttle {\n                emit_progress(downloaded);\n                last_emit = Instant::now();\n            }\n        }\n        file.flush()?;\n        drop(file);\n        emit_progress(downloaded);\n\n        if let Some(expected) = known_total {\n            let actual = partial_path.metadata()?.len();\n            if actual != expected {\n                let _ = fs::remove_file(partial_path);\n                return Err(anyhow::anyhow!(\n                    \"download incomplete: expected {} bytes, got {}\",\n                    expected,\n                    actual\n                ));\n            }\n        }\n\n        // The catalog hash is the trust anchor: for a mirror (an untrusted\n        // host) this verification is what makes the fallback safe at all.\n        Self::verify_file_with_events(model_id, partial_path, expected_sha256, emit).await?;\n        Ok(HttpDownloadOutcome::Completed)\n    }\n}\n\n#[cfg(test)]\nmod tests;\n","sourceCodeStart":350,"sourceCodeEnd":385,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/model/download.rs#L350-L385","documentation":"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.","triggerScenarios":"Server closes early while Content-Length promised more bytes; proxy or CDN truncation; a connection reset surfaced as clean EOF.","commonSituations":"Flaky networks dropping the connection mid-body; servers under load closing streams; antivirus or middleboxes cutting large streaming responses.","solutions":["Retry — the partial is already deleted, so the next attempt is a clean full download","Check for proxy/VPN/AV interference with large streaming responses","Confirm the server serves the whole file: curl -o /dev/null -w '%{size_download}' <url>","Move to a more reliable source (official HF) for multi-GB models"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match downloader.download_http_resumable(...).await {\n    Ok(outcome) => Ok(outcome),\n    Err(e) if e.to_string().starts_with(\"download incomplete\") => {\n        // partial removed; a retry is a clean full download\n        downloader.download_http_resumable(...).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Retry truncation failures automatically — they are overwhelmingly transient","Check for middleboxes (AV, proxies) when truncation recurs on one network only","Cross-check a suspect server with curl -o /dev/null -w '%{size_download}'"],"tags":["truncated-download","size-mismatch","network"],"backgroundTag":"truncated-download","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}