tonhowtf/omniget · error · anyhow::Error

server answered range at byte

Error message

server answered range at byte {} but {} was requested

What it means

Even with a 206 response, download_segment validates the Content-Range start offset against the requested range_start. A 206 for a different offset would be written at the wrong file position, producing a right-sized but corrupt file, so the mismatch is rejected.

Solutions

  1. Inspect the Content-Range header returned by the server for the exact Range request sent
  2. Disable or bypass caching layers/CDN transforms for range requests
  3. Drop and rebuild any persistent connection pools after noticing stale offsets
  4. Retry the segment with a fresh connection; if the server keeps mis-slicing, fall back to single-stream download

Example fix

// request: Range: bytes=100-199
// response: Content-Range: bytes 200-299/1000 -> error
// fix: verify server honors exact offsets
curl -sD - -o /dev/null -H "Range: bytes=100-199" https://example.com/file.bin | grep Content-Range
Defensive patterns

Strategy: validation

Validate before calling

// verify exact offset honoring before trusting the server
let r = client.get(url).header("Range", "bytes=100-199").send().await?;
let cr = r.headers().get("content-range").and_then(|v| v.to_str().ok()).unwrap_or("");
if !cr.starts_with("bytes 100-") { eprintln!("server rewrites ranges; disable segmentation"); }

Type guard

fn content_range_matches(hdr: Option<&reqwest::header::HeaderValue>, want: u64) -> bool {
    hdr.and_then(|h| h.to_str().ok())
       .and_then(|s| s.strip_prefix("bytes "))
       .and_then(|s| s.split('-').next())
       .and_then(|s| s.parse::<u64>().ok())
       .map(|start| start == want)
       .unwrap_or(false)
}

Try / catch

if let Err(e) = segment.download().await {
    if e.to_string().contains("answered range at byte") {
        tracing::warn!("server mis-slices ranges; restarting segment on new connection");
        segment.download_fresh_connection().await?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Server returns 206 whose Content-Range start differs from the requested range_start, e.g. answering bytes=200- instead of the requested bytes=100-, or resuming from a cached/stale offset.

Common situations: Misbehaving CDNs or caching layers that rewrite Content-Range; servers that normalize or clamp the requested range; buggy origin scripts computing their own offsets.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/77dcfb5237419003. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/http_fetcher.rs:980

    let resp = tokio::time::timeout(cfg.connect_timeout, req.send())
        .await
        .map_err(|_| anyhow!("connect timeout"))??;

    let status = resp.status();
    if status != reqwest::StatusCode::PARTIAL_CONTENT {
        if status.is_success() {
            return Err(anyhow!("server did not honor Range (HTTP 200)"));
        }
        return Err(anyhow!("HTTP {}", status));
    }

    // A 206 alone is not proof the server gave us the slice we asked for. If it
    // answers a different offset and we write the body at ours anyway, the file
    // still ends up the right size and is silently corrupt — the one failure
    // mode a segmented download cannot detect later.
    if let Some(start) = content_range_start(resp.headers()) {
        if start != range_start {
            return Err(anyhow!(
                "server answered range at byte {} but {} was requested",
                start,
                range_start
            ));
        }
    }

    if let Some(ct) = resp.headers().get(reqwest::header::CONTENT_TYPE) {
        if let Ok(s) = ct.to_str() {
            if s.contains("text/html") {
                return Err(anyhow!("server returned HTML instead of media"));
            }
        }
    }

    let mut file = tokio::fs::OpenOptions::new()
        .write(true)
        .open(part_path)

View on GitHub (pinned to 8600b91f42)