{"record":{"id":"7a6d89b2f2a91769","repo":"ducaale/xh","slug":"missing-content-range-header","errorCode":null,"errorMessage":"Missing Content-Range header","messagePattern":"Missing Content-Range header","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/download.rs","lineNumber":210,"sourceCode":"\n        dest_name = file_name;\n        buffer = Box::new(open_opts.open(&dest_name)?);\n    } else if test_pretend_term() || io::stdout().is_terminal() {\n        let (new_name, handle) = open_new_file(get_file_name(&response, orig_url).into())?;\n        dest_name = new_name;\n        buffer = Box::new(handle);\n    } else {\n        dest_name = \"<stdout>\".into();\n        buffer = Box::new(io::stdout());\n    }\n\n    let starting_length: u64;\n    let total_length: Option<u64>;\n    if let Some(resume) = resume {\n        let header = response\n            .headers()\n            .get(CONTENT_RANGE)\n            .ok_or_else(|| anyhow!(\"Missing Content-Range header\"))?\n            .to_str()\n            .map_err(|_| anyhow!(\"Bad Content-Range header\"))?;\n        starting_length = resume;\n        total_length = Some(total_for_content_range(header, starting_length)?);\n    } else {\n        starting_length = 0;\n        total_length = get_content_length(response.headers());\n    }\n\n    let starting_time = Instant::now();\n\n    let pb = if quiet {\n        // Still counts the downloaded bytes, it just doesn't display anything.\n        ProgressBar::hidden()\n    } else if let Some(total_length) = total_length {\n        eprintln!(\n            \"Downloading {} to {:?}\",\n            HumanBytes(total_length - starting_length),","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/ducaale/xh/blob/2404aceecc08b0b2d100fedc96f57745cd5904dc/src/download.rs#L192-L228","documentation":"download_file requires a Content-Range header when resuming a download. The server's response to the range request lacked the header entirely, so the client cannot determine the total length or confirm the resume position. Without it, resume bookkeeping is impossible.","triggerScenarios":"download_file called with Some(resume) and the HTTP response headers contain no CONTENT_RANGE entry (server answered without a Content-Range, e.g. with 200 instead of 206).","commonSituations":"Server ignores the Range header and returns 200 with the full body; resuming from servers/proxies that strip range support; remote file was deleted or replaced so no partial response was generated.","solutions":["Restart the download from scratch (offset 0) instead of resuming.","Check the response status: resume only when the server replies 206 Partial Content.","Verify the server supports Range requests (Accept-Ranges: bytes) before resuming.","Bypass intermediaries (proxies/CDNs) that strip or mishandle range headers."],"exampleFix":"// before\nlet total = total_for_content_range(header, resume)?;\n// after\nif response.status() == StatusCode::OK {\n    return restart_download_from_zero();\n}\nlet total = total_for_content_range(header, resume)?;","handlingStrategy":"validation","validationCode":"let resp = client.get(url).header(header::RANGE, format!(\"bytes={}-\", offset)).send()?;\nif resp.status() != StatusCode::PARTIAL_CONTENT || !resp.headers().contains_key(header::CONTENT_RANGE) {\n    // restart from scratch instead of resuming\n}","typeGuard":"fn can_resume(resp: &Response) -> bool {\n    resp.status() == StatusCode::PARTIAL_CONTENT\n        && resp.headers().contains_key(header::CONTENT_RANGE)\n}","tryCatchPattern":"match download_file(url, Some(offset)) {\n    Err(e) if e.to_string().contains(\"Content-Range\") => download_file(url, None),\n    other => other,\n}","preventionTips":["Only resume when the response status is 206 Partial Content.","Check Accept-Ranges: bytes via a HEAD request before resuming.","Handle 200-to-range responses by restarting the download.","Bypass proxies known to strip range headers."],"tags":["download","http","resume","missing-header"],"backgroundTag":"unexpected-response-shape","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"}