ducaale/xh · error

Invalid Content-Range

Error message

Invalid Content-Range: {:?}

What it means

After parsing first_byte_pos, last_byte_pos and optional complete_length out of Content-Range, the tool validates them. This error fires when the header is internally inconsistent: the last byte position precedes the first (first_byte_pos > last_byte_pos), or a supplied complete_length is not greater than last_byte_pos.

Solutions

  1. Delete the partially downloaded file and restart the download from byte 0
  2. Verify the server's Content-Range arithmetic with a manual ranged request (curl -r) and fix/upgrade the server if inconsistent
  3. Disable resume/partial downloads for this host if it's known to emit broken ranges
Defensive patterns

Strategy: try-catch

Validate before calling

fn content_range_sane(first: u64, last: u64, total: Option<u64>) -> bool {
    first <= last && total.map_or(true, |t| last < t)
}

Try / catch

match total_for_content_range(header, expected_start) {
    Ok(total) => resume_from(expected_start, total),
    Err(e) if e.to_string().contains("Invalid Content-Range") => {
        eprintln!("Server sent inconsistent Content-Range; restarting from scratch");
        delete_partial_and_restart();
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Server sends e.g. 'bytes 500-100/1000' where 100 < 500, or 'bytes 0-999/500' where complete_length (500) <= last_byte_pos (999).

Common situations: Buggy download servers computing range arithmetic wrong, caching layers serving mismatched partial content, resume offset out of sync with the file size on the server.

Related errors


AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13). Data as JSON: /api/errors/81996711dfc44daf. Report an issue: GitHub.

Appendix: source

Thrown at src/download.rs:143

        .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")
        })
        .transpose()?;
    // Note that last_byte_pos must be strictly less than complete_length
    // If first_byte_pos == last_byte_pos exactly one byte is sent
    if first_byte_pos > last_byte_pos {
        return Err(anyhow!("Invalid Content-Range: {:?}", header));
    }
    if let Some(complete_length) = complete_length {
        if last_byte_pos >= complete_length {
            return Err(anyhow!("Invalid Content-Range: {:?}", header));
        }
        if complete_length != last_byte_pos + 1 {
            return Err(anyhow!("Content-Range has wrong end: {:?}", header));
        }
    }
    if expected_start != first_byte_pos {
        return Err(anyhow!("Content-Range has wrong start: {:?}", header));
    }
    Ok(last_byte_pos + 1)
}

const BAR_TEMPLATE: &str =
    "{spinner:.green} {percent}% [{wide_bar:.cyan/blue}] {bytes} {bytes_per_sec} ETA {eta}";
const UNCOLORED_BAR_TEMPLATE: &str =

View on GitHub (pinned to 2404aceecc)