{"record":{"id":"b9b9afedb9e1a46e","repo":"actix/actix-web","slug":"invalid-range-header-range-starts-after-end-of-co","errorCode":null,"errorMessage":"invalid Range header: range starts after end of content","messagePattern":"invalid Range header: range starts after end of content","errorType":"http","errorClass":"ParseRangeErr","httpStatus":416,"severity":"warning","filePath":"actix-files/src/range.rs","lineNumber":30,"sourceCode":"impl From<http_range::HttpRangeParseError> for HttpRangeParseError {\n    fn from(err: http_range::HttpRangeParseError) -> Self {\n        match err {\n            http_range::HttpRangeParseError::InvalidRange => Self::InvalidRange,\n            http_range::HttpRangeParseError::NoOverlap => Self::NoOverlap,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Error)]\n#[non_exhaustive]\npub struct ParseRangeErr(#[error(not(source))] HttpRangeParseError);\n\nimpl fmt::Display for ParseRangeErr {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"invalid Range header: \")?;\n        f.write_str(match self.0 {\n            HttpRangeParseError::InvalidRange => \"invalid syntax\",\n            HttpRangeParseError::NoOverlap => \"range starts after end of content\",\n        })\n    }\n}\n\n/// HTTP Range header representation.\n#[derive(Debug, Clone, Copy)]\npub struct HttpRange {\n    /// Start of range.\n    pub start: u64,\n\n    /// Length of range.\n    pub length: u64,\n}\n\nimpl HttpRange {\n    /// Parses Range HTTP header string as per RFC 2616.\n    ///\n    /// `header` is HTTP Range header (e.g. `bytes=0-9`).","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/actix/actix-web/blob/4d435abc281842f3cbee165b6cde739e001d3a25/actix-files/src/range.rs#L12-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-fetch the resource without a Range header to discover its current size, then re-issue a range within bounds.","For resume logic, clamp the requested start to the actual Content-Length returned by a prior HEAD/GET.","Handle the 416 response by restarting the download from byte 0."],"exampleFix":"// before\nRange: bytes=1500-\n// (server file is only 1000 bytes)\n\n// after\nRange: bytes=0-999","handlingStrategy":"validation","validationCode":"// Clamp a resume offset to the known content length before requesting\nfn clamped_range(start: u64, len: u64) -> String {\n    let s = start.min(len);\n    format!(\"bytes={}-\", s)\n}","typeGuard":null,"tryCatchPattern":"// Treat 416 as 'size changed, restart from 0'\nmatch resp.status() {\n    StatusCode::RANGE_NOT_SATISFIABLE => restart_download_from_zero().await,\n    _ => { /* ... */ }\n}","preventionTips":["Re-query Content-Length before resuming.","Clamp requested offsets to the actual file size.","Handle 416 by restarting rather than retrying the same range."],"tags":["http","range-header","actix-files","client-error"],"backgroundTag":null,"analyzedSha":"4d435abc281842f3cbee165b6cde739e001d3a25","analyzedAt":"2026-08-09T01:01:40.926Z","contentChangedAt":"2026-08-09T01:01:40.926Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}