{"record":{"id":"f7778e6e5c0a610f","repo":"tonhowtf/omniget","slug":"incomplete-segment-of-bytes","errorCode":null,"errorMessage":"incomplete segment: {} of {} bytes","messagePattern":"incomplete segment: (.+?) of (.+?) bytes","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/http_fetcher.rs","lineNumber":1081,"sourceCode":"                    written\n                );\n                return Err(anyhow!(\"read timed out after {:?}\", cfg.read_timeout));\n            }\n        }\n    }\n    file.flush().await?;\n    let cur_end = seg.end_ceiling.load(Ordering::Relaxed);\n    let cur_target = (cur_end - begin) + 1;\n    let cur_dl = seg.downloaded.load(Ordering::Relaxed);\n    tracing::debug!(\n        \"[http_fetcher] segment {}: {} bytes in {:.2}s ({})\",\n        seg.id,\n        written,\n        started.elapsed().as_secs_f64(),\n        stop_reason\n    );\n    if cur_dl < cur_target {\n        return Err(anyhow!(\n            \"incomplete segment: {} of {} bytes\",\n            cur_dl,\n            cur_target\n        ));\n    }\n    Ok(())\n}\n\nfn is_fatal(err: &anyhow::Error) -> bool {\n    let m = err.to_string();\n    for code in &[\n        \"HTTP 400\", \"HTTP 401\", \"HTTP 403\", \"HTTP 404\", \"HTTP 405\", \"HTTP 410\", \"HTTP 451\",\n    ] {\n        if m.contains(code) {\n            return true;\n        }\n    }\n    if m.contains(\"HTML instead of media\") {","sourceCodeStart":1063,"sourceCodeEnd":1099,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/http_fetcher.rs#L1063-L1099","documentation":"After the stream ends (or the ceiling is reached), download_segment verifies the number of bytes currently downloaded for the segment equals its current target. If fewer bytes were written than required, the segment is declared incomplete so the worker retries it instead of assembling a corrupt file.","triggerScenarios":"Stream ended (eof or 'ceiling shrank') with written bytes < (end_ceiling+1 - range_start); e.g. server closed the connection early after a partial body, or the stop_reason logic exited before reaching target.","commonSituations":"Origin truncating large files; Content-Length shorter than Range requested; connection drop that reqwest surfaces as clean EOF; misconfigured server clamping ranges.","solutions":["Retry the segment — a fresh ranged GET resumes at the written offset","Compare Content-Length/Content-Range with the requested range to detect server-side truncation","Check disk space and file-descriptor limits if truncation is local (though this path is server-side)","If a specific server always truncates, exclude it or use single-stream download"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// compare advertised size with requested range before trusting a segment\nlet head = client.head(url).send().await?;\nlet len: u64 = head.headers().get(\"content-length\")\n    .and_then(|v| v.to_str().ok()).and_then(|s| s.parse().ok()).unwrap_or(0);\nif len == 0 { eprintln!(\"unknown size; segmented integrity check unreliable\"); }","typeGuard":null,"tryCatchPattern":"if let Err(e) = download_segment(&seg, &cancel).await {\n    if e.to_string().contains(\"incomplete segment\") {\n        tracing::warn!(\"segment truncated by server; retrying from offset\");\n        download_segment(&seg, &cancel).await?;\n    } else { return Err(e.into()); }\n}","preventionTips":["Cap segment retries and mark host unreliable after repeated truncation","Verify final file hash when the source publishes one","Log stop_reason and Content-Range for every segment to diagnose truncation","Prefer hosts with Accept-Ranges + correct Content-Length"],"tags":["http","range-requests","integrity"],"backgroundTag":"http-non-2xx-response","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}