actix/actix-web · warning · ParseRangeErr

invalid Range header: range starts after end of content

Error message

invalid Range header: range starts after end of content

What it means

Returned by HttpRange::parse (range.rs:52) when the parsed range is well-formed but its start offset lies beyond the end of the content (http_range reports NoOverlap). NamedFile::into_response (named.rs:609) catches this and responds 416 Range Not Satisfiable. For example, requesting bytes=15- against a 10-byte file or a reversed range like bytes=5-4 triggers it.

Solutions

  1. Re-fetch the resource without a Range header to discover its current size, then re-issue a range within bounds.
  2. For resume logic, clamp the requested start to the actual Content-Length returned by a prior HEAD/GET.
  3. Handle the 416 response by restarting the download from byte 0.

Example fix

// before
Range: bytes=1500-
// (server file is only 1000 bytes)

// after
Range: bytes=0-999
Defensive patterns

Strategy: validation

Validate before calling

// Clamp a resume offset to the known content length before requesting
fn clamped_range(start: u64, len: u64) -> String {
    let s = start.min(len);
    format!("bytes={}-", s)
}

Try / catch

// Treat 416 as 'size changed, restart from 0'
match resp.status() {
    StatusCode::RANGE_NOT_SATISFIABLE => restart_download_from_zero().await,
    _ => { /* ... */ }
}

Prevention

When it happens

Trigger: Client requests bytes=15- on a file smaller than 15 bytes, or bytes=5-4 (start after end), or bytes=0-2,5-4 on small content. The test cases at range.rs:80-82 cover these.

Common situations: Resuming a download after the file was truncated/replaced with a shorter one; clients caching stale Content-Length and requesting beyond it; off-by-one when computing a suffix range.

Related errors


AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09). Data as JSON: /api/errors/b9b9afedb9e1a46e. Report an issue: GitHub.

Appendix: source

Thrown at actix-files/src/range.rs:30

impl From<http_range::HttpRangeParseError> for HttpRangeParseError {
    fn from(err: http_range::HttpRangeParseError) -> Self {
        match err {
            http_range::HttpRangeParseError::InvalidRange => Self::InvalidRange,
            http_range::HttpRangeParseError::NoOverlap => Self::NoOverlap,
        }
    }
}

#[derive(Debug, Clone, Error)]
#[non_exhaustive]
pub struct ParseRangeErr(#[error(not(source))] HttpRangeParseError);

impl fmt::Display for ParseRangeErr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid Range header: ")?;
        f.write_str(match self.0 {
            HttpRangeParseError::InvalidRange => "invalid syntax",
            HttpRangeParseError::NoOverlap => "range starts after end of content",
        })
    }
}

/// HTTP Range header representation.
#[derive(Debug, Clone, Copy)]
pub struct HttpRange {
    /// Start of range.
    pub start: u64,

    /// Length of range.
    pub length: u64,
}

impl HttpRange {
    /// Parses Range HTTP header string as per RFC 2616.
    ///
    /// `header` is HTTP Range header (e.g. `bytes=0-9`).

View on GitHub (pinned to 4d435abc28)