ducaale/xh · error
Can't parse Content-Range header, can't resume download
Error message
Can't parse Content-Range header, can't resume download
What it means
When resuming a download, the tool parses the server's Content-Range header with a regex expecting 'bytes <start>-<end>/<len|*>'. If the header doesn't match at all — wrong unit like 'items', missing 'bytes' prefix, malformed syntax — it aborts the resume because the byte offset can't be trusted.
Solutions
- Retry the resume; if it still fails, delete the partial file and restart the download from scratch
- Check the actual Content-Range header with -v/curl and confirm the unit is 'bytes' with valid syntax
- Bypass or fix intermediate proxies/CDNs that mangle range headers
Defensive patterns
Strategy: try-catch
Validate before calling
fn parseable_content_range(header: &str) -> bool {
header.starts_with("bytes ") && header.contains('-') && header.contains('/')
} Try / catch
match total_for_content_range(header, expected_start) {
Ok(total) => resume_from(expected_start, total),
Err(e) if e.to_string().contains("Can't parse Content-Range") => {
eprintln!("Resume aborted: {e}; restarting download");
restart_download();
}
Err(e) => return Err(e),
} Prevention
- Inspect the server's raw Content-Range before enabling resume
- Avoid resuming through proxies/CDNs known to rewrite headers
- Fall back to a fresh download when the header is unparsable
When it happens
Trigger: Server returns a non-bytes Content-Range unit, an unparsable or absent range portion (e.g. 'bytes */1234' is handled, but 'items 0-9/10' or 'bytes' alone is not), or a truncated/garbled header from a broken proxy.
Common situations: Resuming against CDNs or caches that rewrite headers, misconfigured reverse proxies stripping or mangling Content-Range, non-HTTP-standard servers on embedded devices.
Related errors
- Invalid Content-Range
- Content-Range has wrong end
- Content-Range has wrong start
- Missing Content-Range header
- Bad Content-Range header
AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13).
Data as JSON: /api/errors/550066c6e7544f30.
Report an issue: GitHub.
Appendix: source
Thrown at src/download.rs:119
if let Some(file) = try_open_new(&candidate)? {
return Ok((candidate, file));
}
}
panic!("Could not create file after unreasonable number of attempts");
}
// https://github.com/httpie/httpie/blob/84c7327057/httpie/downloads.py#L44
// https://tools.ietf.org/html/rfc7233#section-4.2
fn total_for_content_range(header: &str, expected_start: u64) -> Result<u64> {
let re_range = Regex::new(concat!(
r"^bytes (?P<first_byte_pos>\d+)-(?P<last_byte_pos>\d+)",
r"/(?:\*|(?P<complete_length>\d+))$"
))
.unwrap();
let caps = re_range
.captures(header)
// Could happen if header uses unit other than bytes
.ok_or_else(|| anyhow!("Can't parse Content-Range header, can't resume download"))?;
let first_byte_pos: u64 = caps
.name("first_byte_pos")
.unwrap()
.as_str()
.parse()
.context("Can't parse Content-Range first_byte_pos")?;
let last_byte_pos: u64 = caps
.name("last_byte_pos")
.unwrap()
.as_str()
.parse()
.context("Can't parse Content-Range last_byte_pos")?;
let complete_length: Option<u64> = caps
.name("complete_length")
.map(|num| {
num.as_str()
.parse()
.context("Can't parse Content-Range complete_length")View on GitHub (pinned to 2404aceecc)