actix/actix-web · warning · ParseRangeErr
invalid Range header: invalid syntax
Error message
invalid Range header: invalid syntax
What it means
Returned by HttpRange::parse (range.rs:50) when the Range request header fails the bytes= grammar and the underlying http_range crate reports InvalidRange. In actix-files, NamedFile::into_response calls HttpRange::parse on the Range header (named.rs:609) and, on error, sets Content-Range and responds with 416 Range Not Satisfiable. It means the client sent a malformed range specifier such as bytes=foo or bytes=A-Z rather than a valid bytes=start-end expression.
Solutions
- Send a syntactically valid range like bytes=0-499, bytes=500- (open-ended), or bytes=-500 (suffix); or omit the Range header to get the whole file.
- If you control the client, only use the bytes=start-end, bytes=start-, bytes=-suffix, or comma-separated combinations of those forms.
- On the server side, actix-files already auto-responds 416; no change is needed unless you want custom 416 handling.
Example fix
// before curl -H "Range: bytes=5-Z" http://host/file // after curl -H "Range: bytes=0-499" http://host/file
Defensive patterns
Strategy: validation
Validate before calling
// Validate a Range header before relying on it (client side)
fn valid_range(h: &str) -> bool {
let s = h.strip_prefix("bytes=").unwrap_or(h);
!s.is_empty() && s.split(',').all(|p| {
let p = p.trim();
match p.split_once('-') {
Some((a, b)) =>
(a.is_empty() || a.chars().all(|c| c.is_ascii_digit()))
&& (b.is_empty() || b.chars().all(|c| c.is_ascii_digit())),
None => false,
}
})
} Try / catch
// Server: actix-files already turns this into a 416; for custom serving:
match actix_files::range::HttpRange::parse(range_header, file_len) {
Ok(ranges) => { /* serve partial content */ }
Err(_) => response.status(StatusCode::RANGE_NOT_SATISFIABLE).finish(),
} Prevention
- Always emit ranges as bytes=start-end with decimal digits only.
- Prefer a mature HTTP client over hand-built Range headers.
- Log and discard 416s client-side then fall back to a full GET.
When it happens
Trigger: A client sends Range: bytes=5-Z, bytes=foo, bytes=7 (incomplete single value), bytes=0x01-0x02, or bytes=A- . The test suite in range.rs:75-92 enumerates these as rejected inputs.
Common situations: Hand-crafted curl commands with a typo in the Range header; custom download managers or media players emitting non-standard specifiers; an intermediate proxy rewriting the header into an invalid form.
Related errors
- invalid Range header: range starts after end of content
- Invalid character in chunk extension
- Invalid chunk body CR
- Invalid chunk body LF
- Invalid chunk end CR
AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09).
Data as JSON: /api/errors/30b78b1d8c71ea7c.
Report an issue: GitHub.
Appendix: source
Thrown at actix-files/src/range.rs:29
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.
///View on GitHub (pinned to 4d435abc28)