jdx/mise · error

server returned an invalid Content-Range response

Error message

server returned an invalid Content-Range response

What it means

On resume, mise sends a Range request and expects a valid bytes-based Content-Range header describing the returned slice. If the header is missing, malformed, or a non-bytes unit (e.g. */total), and a previous no-resume restart already occurred, it bails with this error after clearing the partial.

Solutions

  1. Bypass/fix the proxy or server middleware so Range responses include a valid 'Content-Range: bytes start-end/total' header.
  2. Clear the partial download and retry from scratch (or from a different mirror).
  3. If the resume offset is beyond the file size (server returns */total), remove the stale partial so the next request starts fresh.
Defensive patterns

Strategy: validation

Validate before calling

curl -fsSI -H 'Range: bytes=10-19' "$url" | grep -i '^content-range: bytes ' || echo "invalid/missing Content-Range"

Prevention

When it happens

Trigger: A resumed download where the server's 206 response lacks a parseable bytes Content-Range (missing header, 'Content-Range: bytes */N' for unsatisfied ranges, or garbage syntax) and the retry-without-resume path also failed the same way.

Common situations: Server or proxy that supports 206 but omits/mangles Content-Range; range requests answered by a cache layer with inconsistent headers; servers responding to out-of-range resumes with the unparseable 'bytes */total' form.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/1bf11274e91962ba. Report an issue: GitHub.

Appendix: source

Thrown at src/http.rs:1151

                if resp.status() == StatusCode::PARTIAL_CONTENT {
                    let Some((state, _)) = resume else {
                        partial.clear()?;
                        if !restarted_without_resume {
                            restarted_without_resume = true;
                            continue;
                        }
                        bail!("server returned partial content without a resumable request");
                    };
                    let content_range = resp
                        .headers()
                        .get(CONTENT_RANGE)
                        .and_then(|value| value.to_str().ok())
                        .and_then(parse_content_range);
                    let Some(ParsedContentRange::Bytes { start, end, total }) = content_range
                    else {
                        partial.clear()?;
                        if restarted_without_resume {
                            bail!("server returned an invalid Content-Range response");
                        }
                        restarted_without_resume = true;
                        continue;
                    };
                    let validator = response_validator(resp.headers());
                    let response_length_matches = resp
                        .content_length()
                        .is_none_or(|length| length == end - start + 1);
                    if start != offset
                        || end + 1 != total
                        || !response_length_matches
                        || state.total_size.is_some_and(|expected| expected != total)
                        || validator
                            .as_ref()
                            .is_some_and(|value| !value.matches(&state.validator))
                    {
                        partial.clear()?;
                        if restarted_without_resume {

View on GitHub (pinned to 533346cc37)