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

Raised by HttpRange::parse (range.rs:50) when the http_range crate returns HttpRangeParseError::NoOverlap: the header parsed successfully but every requested start offset lies beyond the file size. actix-files maps this in NamedFile::into_response (named.rs:609-624) to a 416 Range Not Satisfiable response with 'Content-Range: bytes */<size>'. Syntactically valid, semantically unsatisfiable.

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 937960ca67)

Solutions

  1. Have the client re-fetch Content-Length and restart the range request within bounds.
  2. On the server, ensure the 416 response includes 'Content-Range: bytes */<size>' (actix-files does this automatically) so clients can self-correct.
  3. For dynamic endpoints, validate the requested range against the actual payload length before responding.
  4. Check that the file was not truncated or replaced between HEAD and GET.

Example fix

// before: client uses stale size
Range: bytes=5000-5999   (file is only 1000 bytes)

// after: client clamps to real size or restarts
Range: bytes=0-999
Defensive patterns

Strategy: validation

Validate before calling

// Clamp requested ranges to the known content size before issuing the request.
fn clamp_range(start: u64, end: Option<u64>, size: u64) -> Option<(u64, u64)> {
    if start >= size { return None } // would trigger NoOverlap
    let end = end.map(|e| e.min(size - 1)).unwrap_or(size - 1);
    if start > end { return None }
    Some((start, end))
}

Try / catch

// Same 416 path as syntax errors; actix-files handles it.
match HttpRange::parse(range_header, file_size) {
    Ok(ranges) if !ranges.is_empty() => serve(ranges[0]),
    _ => respond_416(file_size),
}

Prevention

When it happens

Trigger: The resource is small but the client asks for a far-later offset, e.g. file is 1000 bytes and the request is 'Range: bytes=5000-' or 'Range: bytes=1500-2000'. Also occurs when a file was truncated/rotated between the client receiving its size and issuing the range request.

Common situations: Resuming a download after the server file was replaced with a shorter one, requesting a byte range computed from a stale Content-Length, or video players seeking past the end of a media file.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/b9b9afedb9e1a46e.json. Report an issue: GitHub.